You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@struts.apache.org by mr...@apache.org on 2006/01/21 23:00:35 UTC
svn commit: r371121 - in /struts/flow/trunk/src: examples/ examples/WEB-INF/
examples/WEB-INF/classes/ examples/WEB-INF/guess/
examples/WEB-INF/guess/flow/ examples/WEB-INF/guess/jsp/
examples/WEB-INF/rails/ examples/WEB-INF/remote/ examples/WEB-INF/re...
Author: mrdon
Date: Sat Jan 21 14:00:28 2006
New Revision: 371121
URL: http://svn.apache.org/viewcvs?rev=371121&view=rev
Log:
Reorganized examples to use new Ruby on Rails-style controllers, fixed
redirects to be true redirects, changed remote flow calls to use
continuations to access controller methods and values
Added:
struts/flow/trunk/src/examples/WEB-INF/classes/
struts/flow/trunk/src/examples/WEB-INF/classes/commons-logging.properties
struts/flow/trunk/src/examples/WEB-INF/classes/simplelog.properties
struts/flow/trunk/src/examples/WEB-INF/guess/flow/
struts/flow/trunk/src/examples/WEB-INF/guess/flow/NumberGuess.js (with props)
struts/flow/trunk/src/examples/WEB-INF/guess/jsp/
struts/flow/trunk/src/examples/WEB-INF/guess/jsp/endGame.jsp (with props)
struts/flow/trunk/src/examples/WEB-INF/guess/jsp/play.jsp (with props)
struts/flow/trunk/src/examples/WEB-INF/remote/flow/
struts/flow/trunk/src/examples/WEB-INF/remote/flow/NumberGuess.js (with props)
struts/flow/trunk/src/examples/WEB-INF/remote/jsp/
struts/flow/trunk/src/examples/WEB-INF/remote/jsp/endGame.jsp (with props)
struts/flow/trunk/src/examples/WEB-INF/remote/jsp/play.jsp (with props)
struts/flow/trunk/src/examples/WEB-INF/templates/flow/
struts/flow/trunk/src/examples/WEB-INF/templates/flow/NumberGuess.js (with props)
struts/flow/trunk/src/examples/WEB-INF/templates/jt/
struts/flow/trunk/src/examples/WEB-INF/templates/jt/endGame.jt (with props)
struts/flow/trunk/src/examples/WEB-INF/templates/jt/play.jt (with props)
struts/flow/trunk/src/examples/WEB-INF/wizard/flow/
struts/flow/trunk/src/examples/WEB-INF/wizard/flow/Registration.js (with props)
struts/flow/trunk/src/examples/WEB-INF/wizard/flow/wizard.js (with props)
struts/flow/trunk/src/examples/WEB-INF/wizard/jsp/
struts/flow/trunk/src/examples/WEB-INF/wizard/jsp/hobbies-form.jsp (with props)
struts/flow/trunk/src/examples/WEB-INF/wizard/jsp/name-form.jsp (with props)
struts/flow/trunk/src/examples/WEB-INF/wizard/jsp/summary-form.jsp (with props)
struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/template.js
- copied unchanged from r360243, struts/flow/trunk/src/examples/templates/template.js
Removed:
struts/flow/trunk/src/examples/WEB-INF/guess/guess.jsp
struts/flow/trunk/src/examples/WEB-INF/guess/numberguess.js
struts/flow/trunk/src/examples/WEB-INF/guess/struts-config.xml
struts/flow/trunk/src/examples/WEB-INF/guess/success.jsp
struts/flow/trunk/src/examples/WEB-INF/rails/
struts/flow/trunk/src/examples/WEB-INF/remote/guess.jsp
struts/flow/trunk/src/examples/WEB-INF/remote/numberguess.js
struts/flow/trunk/src/examples/WEB-INF/remote/struts-config.xml
struts/flow/trunk/src/examples/WEB-INF/remote/success.jsp
struts/flow/trunk/src/examples/WEB-INF/struts-config.xml
struts/flow/trunk/src/examples/WEB-INF/templates/guess.jt
struts/flow/trunk/src/examples/WEB-INF/templates/numberguess.js
struts/flow/trunk/src/examples/WEB-INF/templates/struts-config.xml
struts/flow/trunk/src/examples/WEB-INF/templates/success.jt
struts/flow/trunk/src/examples/WEB-INF/wizard/hobbies-form.jsp
struts/flow/trunk/src/examples/WEB-INF/wizard/name-form.jsp
struts/flow/trunk/src/examples/WEB-INF/wizard/struts-config.xml
struts/flow/trunk/src/examples/WEB-INF/wizard/summary-form.jsp
struts/flow/trunk/src/examples/WEB-INF/wizard/wizard-flow.js
struts/flow/trunk/src/examples/WEB-INF/wizard/wizard.js
Modified:
struts/flow/trunk/src/examples/index.html
struts/flow/trunk/src/java/org/apache/struts/flow/FlowAction.java
struts/flow/trunk/src/java/org/apache/struts/flow/FlowPlugIn.java
struts/flow/trunk/src/java/org/apache/struts/flow/Forward.java
struts/flow/trunk/src/java/org/apache/struts/flow/core/Interpreter.java
struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOM_Flow.java
struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOM_JavaScriptInterpreter.java
struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/fom_system.js
Added: struts/flow/trunk/src/examples/WEB-INF/classes/commons-logging.properties
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/examples/WEB-INF/classes/commons-logging.properties?rev=371121&view=auto
==============================================================================
--- struts/flow/trunk/src/examples/WEB-INF/classes/commons-logging.properties (added)
+++ struts/flow/trunk/src/examples/WEB-INF/classes/commons-logging.properties Sat Jan 21 14:00:28 2006
@@ -0,0 +1,2 @@
+org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog
+org.apache.commons.logging.simplelog.defaultlog=debug
Added: struts/flow/trunk/src/examples/WEB-INF/classes/simplelog.properties
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/examples/WEB-INF/classes/simplelog.properties?rev=371121&view=auto
==============================================================================
--- struts/flow/trunk/src/examples/WEB-INF/classes/simplelog.properties (added)
+++ struts/flow/trunk/src/examples/WEB-INF/classes/simplelog.properties Sat Jan 21 14:00:28 2006
@@ -0,0 +1 @@
+org.apache.commons.logging.simplelog.defaultlog=debug
Added: struts/flow/trunk/src/examples/WEB-INF/guess/flow/NumberGuess.js
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/examples/WEB-INF/guess/flow/NumberGuess.js?rev=371121&view=auto
==============================================================================
--- struts/flow/trunk/src/examples/WEB-INF/guess/flow/NumberGuess.js (added)
+++ struts/flow/trunk/src/examples/WEB-INF/guess/flow/NumberGuess.js Sat Jan 21 14:00:28 2006
@@ -0,0 +1,39 @@
+NumberGuess = function() {
+
+ this.play = function() {
+
+ this.random = Math.round( Math.random() * 9 ) + 1;
+ this.hint = "No hint for you!"
+ this.guesses = 0;
+
+ while (true) {
+
+ // send guess page to user and wait for response
+ flow.wait();
+
+ // process user's guess
+ this.guess = parseInt( params.guess );
+ this.guesses++;
+ if (this.guess) {
+ if (this.guess > this.random) {
+ this.hint = "Nope, lower!"
+ }
+ else if (this.guess < this.random) {
+ this.hint = "Nope, higher!"
+ }
+ else {
+ // correct guess
+ break;
+ }
+ }
+ }
+
+ // send success page to user
+ flash.guesses = this.guesses;
+ flash.random = this.random;
+
+ flow.redirect( { "action" : "endGame" } );
+ }
+
+ this.endGame = function() {}
+}
Propchange: struts/flow/trunk/src/examples/WEB-INF/guess/flow/NumberGuess.js
------------------------------------------------------------------------------
svn:executable = *
Added: struts/flow/trunk/src/examples/WEB-INF/guess/jsp/endGame.jsp
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/examples/WEB-INF/guess/jsp/endGame.jsp?rev=371121&view=auto
==============================================================================
--- struts/flow/trunk/src/examples/WEB-INF/guess/jsp/endGame.jsp (added)
+++ struts/flow/trunk/src/examples/WEB-INF/guess/jsp/endGame.jsp Sat Jan 21 14:00:28 2006
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<html>
+<head>
+ <title>Struts Flow number guessing game</title>
+</head>
+<body>
+
+ <h1>Success!</h1>
+
+ <h2>The number was: <%= request.getAttribute("random") %></h2>
+
+ <h3>It took you <%= request.getAttribute("guesses") %> tries.</h3>
+
+ <p><a href="play.do">Play again</a></p>
+
+ <a href="../../index.html">Return to index</a>
+</body>
+</html>
Propchange: struts/flow/trunk/src/examples/WEB-INF/guess/jsp/endGame.jsp
------------------------------------------------------------------------------
svn:executable = *
Added: struts/flow/trunk/src/examples/WEB-INF/guess/jsp/play.jsp
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/examples/WEB-INF/guess/jsp/play.jsp?rev=371121&view=auto
==============================================================================
--- struts/flow/trunk/src/examples/WEB-INF/guess/jsp/play.jsp (added)
+++ struts/flow/trunk/src/examples/WEB-INF/guess/jsp/play.jsp Sat Jan 21 14:00:28 2006
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<html>
+<head>
+ <title>Struts Flow number guessing game</title>
+</head>
+<body>
+
+ <h1>Guess the Number Between 1 and 10</h1>
+
+ <h2><%= request.getAttribute("hint") %></h2>
+
+ <h3>You've guessed <%= request.getAttribute("guesses") %> times.</h3>
+
+ <form method="post" action="play.do">
+ <input type="hidden" name="contid" value='<%= request.getAttribute("contid") %>' />
+ <input type="text" name="guess"/>
+ <input type="submit"/>
+ </form>
+ <a href="../../index.html">Return to index</a>
+</body>
+</html>
Propchange: struts/flow/trunk/src/examples/WEB-INF/guess/jsp/play.jsp
------------------------------------------------------------------------------
svn:executable = *
Added: struts/flow/trunk/src/examples/WEB-INF/remote/flow/NumberGuess.js
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/examples/WEB-INF/remote/flow/NumberGuess.js?rev=371121&view=auto
==============================================================================
--- struts/flow/trunk/src/examples/WEB-INF/remote/flow/NumberGuess.js (added)
+++ struts/flow/trunk/src/examples/WEB-INF/remote/flow/NumberGuess.js Sat Jan 21 14:00:28 2006
@@ -0,0 +1,44 @@
+NumberGuess = function() {
+
+ this.play = function() {
+
+ this.random = Math.round( Math.random() * 9 ) + 1;
+ this.hint = "No hint for you!"
+ this.guesses = 0;
+
+ while (true) {
+
+ // send guess page to user and wait for response
+ flow.wait();
+
+ // process user's guess
+ this.guess = parseInt( params.guess );
+ this.guesses++;
+ if (this.guess) {
+ if (this.guess > this.random) {
+ this.hint = "Nope, lower!"
+ }
+ else if (this.guess < this.random) {
+ this.hint = "Nope, higher!"
+ }
+ else {
+ // correct guess
+ break;
+ }
+ }
+ }
+
+ // send success page to user
+ flash.guesses = this.guesses;
+ flash.random = this.random;
+
+ flow.redirect( { "action" : "endGame" } );
+ }
+
+ this.endGame = function() {}
+
+ this.cheat = function() {
+ this.guesses += 5;
+ return {"secret":this.random, "guesses":this.guesses};
+ }
+}
Propchange: struts/flow/trunk/src/examples/WEB-INF/remote/flow/NumberGuess.js
------------------------------------------------------------------------------
svn:executable = *
Added: struts/flow/trunk/src/examples/WEB-INF/remote/jsp/endGame.jsp
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/examples/WEB-INF/remote/jsp/endGame.jsp?rev=371121&view=auto
==============================================================================
--- struts/flow/trunk/src/examples/WEB-INF/remote/jsp/endGame.jsp (added)
+++ struts/flow/trunk/src/examples/WEB-INF/remote/jsp/endGame.jsp Sat Jan 21 14:00:28 2006
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<html>
+<head>
+ <title>Struts Flow number guessing game - Remote edition</title>
+</head>
+<body>
+
+ <h1>Success!</h1>
+
+ <h2>The number was: <%= request.getAttribute("random") %></h2>
+
+ <h3>It took you <%= request.getAttribute("guesses") %> tries.</h3>
+
+ <p><a href="play.do">Play again</a></p>
+
+ <a href="../../index.html">Return to index</a>
+</body>
+</html>
Propchange: struts/flow/trunk/src/examples/WEB-INF/remote/jsp/endGame.jsp
------------------------------------------------------------------------------
svn:executable = *
Added: struts/flow/trunk/src/examples/WEB-INF/remote/jsp/play.jsp
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/examples/WEB-INF/remote/jsp/play.jsp?rev=371121&view=auto
==============================================================================
--- struts/flow/trunk/src/examples/WEB-INF/remote/jsp/play.jsp (added)
+++ struts/flow/trunk/src/examples/WEB-INF/remote/jsp/play.jsp Sat Jan 21 14:00:28 2006
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<html>
+<head>
+ <title>Struts Flow number guessing game - Remote edition</title>
+ <script type="text/javascript">
+ <!--
+function cheat() {
+ dojo.io.bind({
+ url: 'play.do?FlowCall=cheat&contid=<%= request.getAttribute("contid") %>',
+ type: "text/javascript",
+ load: function(type, data, evt) {
+ eval("data = "+data);
+ alert("The secret number is "+data.secret+". After applying a penalty, you have guessed "+data.guesses+" times");
+ }
+ });
+}
+ -->
+ </script>
+ <script type="text/javascript" src="../dojo-io.js"></script>
+</head>
+<body>
+
+ <h1>Guess the Number Between 1 and 10</h1>
+
+ <h2><%= request.getAttribute("hint") %></h2>
+
+ <h3>You've guessed <%= request.getAttribute("guesses") %> times.</h3>
+
+ <form method="post" action="play.do">
+ <input type="hidden" name="contid" value='<%= request.getAttribute("contid") %>' />
+ <input type="text" name="guess"/>
+ <input type="submit"/>
+ <input type="button" onclick="cheat()" value="Cheat" />
+ </form>
+
+ <a href="../../index.html">Return to index</a>
+</body>
+</html>
Propchange: struts/flow/trunk/src/examples/WEB-INF/remote/jsp/play.jsp
------------------------------------------------------------------------------
svn:executable = *
Added: struts/flow/trunk/src/examples/WEB-INF/templates/flow/NumberGuess.js
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/examples/WEB-INF/templates/flow/NumberGuess.js?rev=371121&view=auto
==============================================================================
--- struts/flow/trunk/src/examples/WEB-INF/templates/flow/NumberGuess.js (added)
+++ struts/flow/trunk/src/examples/WEB-INF/templates/flow/NumberGuess.js Sat Jan 21 14:00:28 2006
@@ -0,0 +1,71 @@
+NumberGuess = function() {
+
+ this.play = function() {
+
+ this.random = Math.round( Math.random() * 9 ) + 1;
+ this.hint = "No hint for you!"
+ this.guesses = 0;
+
+ while (true) {
+
+ // send guess page to user and wait for response
+ flow.wait("play", this);
+
+ // process user's guess
+ this.guess = parseInt( params.guess );
+ this.guesses++;
+ if (this.guess) {
+ if (this.guess > this.random) {
+ this.hint = "Nope, lower!"
+ }
+ else if (this.guess < this.random) {
+ this.hint = "Nope, higher!"
+ }
+ else {
+ // correct guess
+ break;
+ }
+ }
+ }
+
+ // send success page to user
+ flash.guesses = this.guesses;
+ flash.random = this.random;
+
+ flow.redirect( { "action" : "endGame" } );
+ }
+
+ this.endGame = function() {
+ renderTemplate("endGame", this);
+ }
+
+ this.cheat = function() {
+ this.guesses += 5;
+ return {"secret":this.random, "guesses":this.guesses};
+ }
+}
+
+
+// This function renders the content directly using Javascript Templates
+function renderTemplateAndWait(page, bizdata, ttl) {
+ var cont = new FOM_WebContinuation(new Continuation(), flow.continuation, ttl);
+ bizdata.contid = cont.id;
+ renderTemplate(page, bizdata);
+ flow.forward(null, bizdata, cont);
+ FOM_Flow.suicide();
+}
+
+function renderTemplate(page, bizdata) {
+ var res = flow.context.response;
+ var stream = struts.servletContext.getResourceAsStream("/WEB-INF/templates/jt/"+page+".jt");
+ if (stream != null) {
+ var text = new String(stream.getText());
+ var html = text.process(bizdata);
+ res.writer.print(html);
+ res.writer.close();
+ } else {
+ res.sendError(res.SC_INTERNAL_SERVER_ERROR, "Unable to find page "+page);
+ }
+}
+
+FOM_Flow.prototype._wait=renderTemplateAndWait;
Propchange: struts/flow/trunk/src/examples/WEB-INF/templates/flow/NumberGuess.js
------------------------------------------------------------------------------
svn:executable = *
Added: struts/flow/trunk/src/examples/WEB-INF/templates/jt/endGame.jt
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/examples/WEB-INF/templates/jt/endGame.jt?rev=371121&view=auto
==============================================================================
--- struts/flow/trunk/src/examples/WEB-INF/templates/jt/endGame.jt (added)
+++ struts/flow/trunk/src/examples/WEB-INF/templates/jt/endGame.jt Sat Jan 21 14:00:28 2006
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<html>
+<head>
+ <title>Struts Flow number guessing game</title>
+</head>
+<body>
+
+ <h1>Success!</h1>
+
+ <h2>The number was: ${random}</h2>
+
+ <h3>It took you ${guesses} tries.</h3>
+
+ <p><a href="play.do">Play again</a></p>
+
+ <a href="../index.html">Return to index</a>
+</body>
+</html>
Propchange: struts/flow/trunk/src/examples/WEB-INF/templates/jt/endGame.jt
------------------------------------------------------------------------------
svn:executable = *
Added: struts/flow/trunk/src/examples/WEB-INF/templates/jt/play.jt
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/examples/WEB-INF/templates/jt/play.jt?rev=371121&view=auto
==============================================================================
--- struts/flow/trunk/src/examples/WEB-INF/templates/jt/play.jt (added)
+++ struts/flow/trunk/src/examples/WEB-INF/templates/jt/play.jt Sat Jan 21 14:00:28 2006
@@ -0,0 +1,48 @@
+<?xml version="1.0"?>
+<html>
+<head>
+ <title>Struts Flow number guessing game</title>
+ <script type="text/javascript">
+ <!--
+function cheat() {
+ hint = document.getElementById("hint");
+
+ dojo.io.bind({
+ url: 'play.do?FlowCall=cheat&contid=${contid}',
+ type: "text/javascript",
+ load: function(type, data, evt) {
+ eval("data = "+data);
+ dojo.io.bind({
+ url: "../cheat.jt",
+ type: "text/plain",
+ load: function(type, temp, evt) {
+ hint.innerHTML = temp.process(data);
+ }
+ });
+ }
+ });
+}
+ -->
+ </script>
+ <script type="text/javascript" src="../../remote/dojo-io.js"></script>
+ <script type="text/javascript" src="../template.js"></script>
+</head>
+<body>
+
+ <h1>Guess the Number Between 1 and 10</h1>
+
+ <h2 id="hint">${hint}</h2>
+
+ <h3>You've guessed ${guesses} times.</h3>
+
+ <form method="post" action="play.do">
+ <input type="hidden" name="contid" value="${contid}" />
+ <input type="text" name="guess"/>
+ <input type="submit"/>
+ <input type="button" onclick="cheat()" value="Cheat" />
+ </form>
+
+ <a href="../../index.html">Return to index</a>
+
+</body>
+</html>
Propchange: struts/flow/trunk/src/examples/WEB-INF/templates/jt/play.jt
------------------------------------------------------------------------------
svn:executable = *
Added: struts/flow/trunk/src/examples/WEB-INF/wizard/flow/Registration.js
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/examples/WEB-INF/wizard/flow/Registration.js?rev=371121&view=auto
==============================================================================
--- struts/flow/trunk/src/examples/WEB-INF/wizard/flow/Registration.js (added)
+++ struts/flow/trunk/src/examples/WEB-INF/wizard/flow/Registration.js Sat Jan 21 14:00:28 2006
@@ -0,0 +1,39 @@
+flow.load("/WEB-INF/wizard/flow/wizard.js");
+
+Registration = function() {
+
+ this.start = function() {
+ var model = new java.util.HashMap();
+
+ var wizard = new Wizard(model);
+ wizard.populate = populate;
+ wizard.validate = validate;
+
+ wizard.showForm( { "action" : "name-form" }, {
+ "title" : "User Name Information"
+ });
+ wizard.showForm( { "action" : "hobbies-form" }, {
+ "title" : "User Hobbies"
+ });
+ wizard.showForm( { "action" : "summary-form" } , {
+ "title" : "User Summary"
+ });
+ };
+}
+
+function populate() {
+ var m = struts.paramValues;
+ for (var i = m.keySet().iterator(); i.hasNext(); ) {
+ var key = i.next();
+ this.model.put(key, m.get(key)[0]);
+ }
+ // Bug in commons-chain prevents this
+ //this.model.putAll(struts.paramValues);
+}
+
+function validate() {
+ if (this.model.get("name").length() < 2) {
+ return "Name must be specified";
+ }
+}
+
Propchange: struts/flow/trunk/src/examples/WEB-INF/wizard/flow/Registration.js
------------------------------------------------------------------------------
svn:executable = *
Added: struts/flow/trunk/src/examples/WEB-INF/wizard/flow/wizard.js
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/examples/WEB-INF/wizard/flow/wizard.js?rev=371121&view=auto
==============================================================================
--- struts/flow/trunk/src/examples/WEB-INF/wizard/flow/wizard.js (added)
+++ struts/flow/trunk/src/examples/WEB-INF/wizard/flow/wizard.js Sat Jan 21 14:00:28 2006
@@ -0,0 +1,180 @@
+/*
+ * This file provides a Wizard object that can be used to easily implement
+ * multi-page workflows, commonly called wizards. The following features
+ * are built into the framework:
+ *
+ * - Pluggable validation
+ * - Pluggable data population and post-population processing
+ * - Automatic forward/backward navigation handling
+ * - Capable of handing any model (POJO, DynaBean, JDOM Element, etc)
+ *
+ * To insert your own logic, particularly validation and population logic,
+ * simply the desired methods with your own.
+ */
+
+ /**
+ * Constructor for the Wizard.
+ *
+ * @param model The data model object to store form data
+ */
+function Wizard(model) {
+
+ // The data model, can be anything
+ this.model = model;
+
+ // The time to live for forms, defaults to continuation manager settings
+ this.timeToLive = 0;
+
+ // The key the model is placed under
+ this.modelKey = "form";
+
+ // The key the errors are placed under
+ this.errorsKey = "errors";
+
+ // The name of the request parameter signifying the "Next" action
+ this.nextId = "next";
+}
+
+/**
+ * Called to populate the model with the request
+ */
+Wizard.prototype.populate = function() {}
+
+/**
+ * Called to handle any post-processing after populate has been called
+ */
+Wizard.prototype.postPopulate = function() {}
+
+/**
+ * Called to validate the form
+ *
+ * @param frmName The name of the form to validate
+ * @return Any errors, null if none
+ */
+Wizard.prototype.validate = function(frmName) {}
+
+/**
+ * Called to prepare the model right before the form is sent.
+ *
+ * @return The model to include in the business data passed to the form
+ */
+Wizard.prototype.prepareModel = function() {return this.model;}
+
+/**
+ * Creates a continuation, saves the context, and sends the form. Shouldn't
+ * need to be called from outside the Wizard class.
+ *
+ * @param name The name of the form to send
+ * @param lastWebCont The parent web continuation
+ * @param bizdata A map of objects to pass to the form
+ */
+Wizard.prototype.sendFormAndWait = function(name, lastWebCont, bizdata, ttl) {
+ flow.forward(name, bizdata, new FOM_WebContinuation(new Continuation(), lastWebCont, ttl));
+ flow.exit();
+}
+
+/**
+ * Shows the form, handling validation and navigation.
+ *
+ * @param doValidate Whether to validate or not (optional)
+ * @param exitIds An array of submit button names that allow "next" behavior
+ * @return The submit button name that was pressed
+ */
+Wizard.prototype.showForm = function(frm, bizdata, doValidate, exitIds) {
+
+ // Default validation to true if not passed
+ var doValidate = (doValidate == null ? true : doValidate);
+
+ // Default to the next id if none passed
+ var exitIds = (exitIds == null ? new Array(this.nextId) : exitIds);
+
+ var lastWebCont = flow.continuation;
+ // create a continuation, the invocation of which will resend
+ // the page: this is used to implement the back button
+ var wk = new FOM_WebContinuation(new Continuation(), lastWebCont);
+ flow.log.debug("saving spot "+wk.id+" before form:"+frm);
+
+ // Loop to keep showing form until validation passes and next button
+ // is pressed
+ var keepShowing = true;
+ var thisWebCont;
+ var tmpModel;
+ var exitId;
+ while (keepShowing) {
+ keepShowing = false;
+
+ // Wraps the model before attaching to bizdata to allow any necessary
+ // cloning or ActionForm wrapping
+ tmpModel = this.prepareModel(this.model);
+ bizdata[this.modelKey] = tmpModel;
+
+ // Send the form and wait
+ thisWebCont = this.sendFormAndWait(frm, wk, bizdata);
+
+ // Populate the model with form submission
+ this.populate();
+ this.postPopulate();
+
+ // If validation is enabled, validate and determine if should keep
+ // showing
+ if (doValidate) {
+ var errors = this.validate(frm);
+ if (errors == null || errors.length == 0) {
+ bizdata[this.errorsKey] = null;
+ } else {
+ bizdata[this.errorsKey] = errors;
+ keepShowing = true;
+ }
+ }
+
+ // Determine if next button is pressed and should stop showing
+ if ((doValidate && !keepShowing) || !doValidate) {
+ keepShowing = true;
+ var params = struts.param;
+ for (var id in exitIds) {
+ if (params[exitIds[id]] != null || params[exitIds[id]+'.x'] != null) {
+ exitId = exitIds[id];
+ keepShowing = false;
+ break;
+ }
+ }
+ }
+ }
+ flow.continuationn = thisWebCont;
+ return exitId;
+}
+
+
+/**
+ * This function is called to restart a previously saved continuation
+ * passed as argument. Overrides the default handleContinuation to
+ * add support for back buttons.
+ *
+ * @param kont The continuation to restart
+ */
+function handleContinuation(k, wk) {
+
+ // This can be overridden by declaring a "prevId" variable outside the function
+ var prevId = (this.prevId != null ? this.prevId : "prev");
+
+ flow.log.debug("Previous Id:"+struts.param[prevId]+" cont:"+wk.id);
+ if (struts.param[prevId]) {
+ var cont = wk;
+ for (var x=0; x<2; x++) {
+ if (cont == null) {
+ flow.log.error("can't get parent continuation, back "+x);
+ break;
+ } else {
+ cont = cont.getParent();
+ }
+ }
+ if (cont != null) {
+ cont.continuation(cont);
+ } else {
+ k(wk);
+ }
+ } else {
+ k(wk);
+ }
+}
+flow.handleContinuation = handleContinuation;
Propchange: struts/flow/trunk/src/examples/WEB-INF/wizard/flow/wizard.js
------------------------------------------------------------------------------
svn:executable = *
Added: struts/flow/trunk/src/examples/WEB-INF/wizard/jsp/hobbies-form.jsp
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/examples/WEB-INF/wizard/jsp/hobbies-form.jsp?rev=371121&view=auto
==============================================================================
--- struts/flow/trunk/src/examples/WEB-INF/wizard/jsp/hobbies-form.jsp (added)
+++ struts/flow/trunk/src/examples/WEB-INF/wizard/jsp/hobbies-form.jsp Sat Jan 21 14:00:28 2006
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<html>
+<head>
+ <title><%=request.getAttribute("title")%></title>
+</head>
+<body>
+
+ <h1><%=request.getAttribute("title")%></h1>
+ <p>
+ Enter your hobbies:
+ </p>
+
+ <center style="color:red"><%=(request.getAttribute("errors") != null ? request.getAttribute("errors") : "")%></center>
+
+ <% java.util.Map form = (java.util.Map)request.getAttribute("form"); %>
+ <form action="registration.do" method="POST">
+ <table>
+ <tr>
+ <th>Favorite Sport</th>
+ <td><input type="text" name="sport" value="<%=(form.get("sport") != null ? form.get("sport") : "")%>"/></td>
+ </tr>
+
+ <tr>
+ <th>Favorite Book</th>
+ <td><input type="text" name="book" value="<%=(form.get("book") != null ? form.get("book") : "")%>"/></td>
+ </tr>
+
+ </table>
+
+ <input type="hidden" name="contid" value='<%= request.getAttribute("contid") %>' />
+ <input type="submit" name="prev" value="Previous" />
+ <input type="submit" name="next" value="Next" />
+ </form>
+
+ <a href="../../index.html">Return to index</a>
+</body>
+</html>
Propchange: struts/flow/trunk/src/examples/WEB-INF/wizard/jsp/hobbies-form.jsp
------------------------------------------------------------------------------
svn:executable = *
Added: struts/flow/trunk/src/examples/WEB-INF/wizard/jsp/name-form.jsp
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/examples/WEB-INF/wizard/jsp/name-form.jsp?rev=371121&view=auto
==============================================================================
--- struts/flow/trunk/src/examples/WEB-INF/wizard/jsp/name-form.jsp (added)
+++ struts/flow/trunk/src/examples/WEB-INF/wizard/jsp/name-form.jsp Sat Jan 21 14:00:28 2006
@@ -0,0 +1,40 @@
+<?xml version="1.0"?>
+<html>
+<head>
+ <title><%=request.getAttribute("title")%></title>
+</head>
+<body>
+
+ <h1><%=request.getAttribute("title")%></h1>
+ <p>
+ Enter your name information:
+ </p>
+
+ <center style="color:red"><%=(request.getAttribute("errors") != null ? request.getAttribute("errors") : "")%></center>
+ <form action="registration.do" method="POST">
+
+ <% java.util.Map form = (java.util.Map)request.getAttribute("form"); %>
+ <table>
+ <tr>
+ <th>First Name</th>
+ <td><input type="text" name="name" value="<%=(form.get("name") != null ? form.get("name") : "")%>"/></td>
+ </tr>
+
+ <tr>
+ <th>Last Name</th>
+ <td><input type="text" name="lastname" value="<%=(form.get("lastname") != null ? form.get("lastname") : "")%>"/></td>
+ </tr>
+
+ <tr>
+ <th>Middle Name</th>
+ <td><input type="text" name="middlename" value="<%=(form.get("middlename") != null ? form.get("middlename") : "")%>"/></td>
+ </tr>
+ </table>
+
+ <input type="hidden" name="contid" value='<%= request.getAttribute("contid") %>' />
+ <input type="submit" name="next" value="Next" />
+ </form>
+
+ <a href="../../index.html">Return to index</a>
+</body>
+</html>
Propchange: struts/flow/trunk/src/examples/WEB-INF/wizard/jsp/name-form.jsp
------------------------------------------------------------------------------
svn:executable = *
Added: struts/flow/trunk/src/examples/WEB-INF/wizard/jsp/summary-form.jsp
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/examples/WEB-INF/wizard/jsp/summary-form.jsp?rev=371121&view=auto
==============================================================================
--- struts/flow/trunk/src/examples/WEB-INF/wizard/jsp/summary-form.jsp (added)
+++ struts/flow/trunk/src/examples/WEB-INF/wizard/jsp/summary-form.jsp Sat Jan 21 14:00:28 2006
@@ -0,0 +1,48 @@
+<?xml version="1.0"?>
+<html>
+<head>
+ <title><%=request.getAttribute("title")%></title>
+</head>
+<body>
+
+ <h1><%=request.getAttribute("title")%></h1>
+ <p>
+ Congratulations!
+ </p>
+
+ <% java.util.Map form = (java.util.Map)request.getAttribute("form"); %>
+ <table border="1">
+ <tr>
+ <th>First Name</th>
+ <td><%=form.get("name")%></td>
+ </tr>
+
+ <tr>
+ <th>Last Name</th>
+ <td><%=form.get("lastname")%></td>
+ </tr>
+
+ <tr>
+ <th>Middle Name</th>
+ <td><%=form.get("middlename")%></td>
+ </tr>
+
+ <tr>
+ <th>Favorite Sport</th>
+ <td><%=form.get("sport")%></td>
+ </tr>
+
+ <tr>
+ <th>Favorite Book</th>
+ <td><%=form.get("book")%></td>
+ </tr>
+ </table>
+
+ <form action="registration.do" method="POST">
+ <input type="hidden" name="contid" value='<%= request.getAttribute("contid") %>' />
+ <input type="submit" name="prev" value="Previous" />
+ </form>
+
+ <a href="../../index.html">Return to index</a>
+</body>
+</html>
Propchange: struts/flow/trunk/src/examples/WEB-INF/wizard/jsp/summary-form.jsp
------------------------------------------------------------------------------
svn:executable = *
Modified: struts/flow/trunk/src/examples/index.html
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/examples/index.html?rev=371121&r1=371120&r2=371121&view=diff
==============================================================================
--- struts/flow/trunk/src/examples/index.html (original)
+++ struts/flow/trunk/src/examples/index.html Sat Jan 21 14:00:28 2006
@@ -8,21 +8,18 @@
in Struts Flow:
</p>
<ul>
- <li><a href="guess/guess.do">Number Guess Example</a> - Shows a simple
+ <li><a href="guess/NumberGuess/play.do">Number Guess Example</a> - Shows a simple
number guessing game to demonstrate the use of continuations.</li>
- <li><a href="remote/guess.do">Number Guess Example - Remote Edition</a> -
+ <li><a href="remote/NumberGuess/play.do">Number Guess Example - Remote Edition</a> -
Shows the above example but adding a remote rpc or (Ajax) example by
adding a "cheat" button that retrieves the number from the server
without reloading the page.</li>
- <li><a href="templates/guess.do">Number Guess Example - Template Edition</a> -
+ <li><a href="templates/NumberGuess/play.do">Number Guess Example - Template Edition</a> -
Shows the remote example but using <a
href="http://www.trimpath.com/project/wiki/JavaScriptTemplates">Javascript
Templates</a> to render HTML on both the server instead of JSP and client
instead of string contatenations.</li>
- <li><a href="rails/NumberGuess/play.do">Number Guess Example - Rails Edition</a> - Shows the
- game but written using <a href="http://www.rubyonrails.org">Ruby on Rails</a>-style
- controllers with a zero-configuration layout.</li>
- <li><a href="wizard/registration.do">Registration Wizard Example</a> -
+ <li><a href="wizard/Registration/start.do">Registration Wizard Example</a> -
Shows a simple server-side wizard framework built on continuations
with automatic back button support both from a form and browser
button.</li>
Modified: struts/flow/trunk/src/java/org/apache/struts/flow/FlowAction.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/FlowAction.java?rev=371121&r1=371120&r2=371121&view=diff
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/FlowAction.java (original)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/FlowAction.java Sat Jan 21 14:00:28 2006
@@ -113,13 +113,8 @@
// A FlowCall means the request came from client-side javascript that
// expects the return type to be JSON
boolean isFlowCall = (request.getParameter("FlowCall") != null);
- String func = null, controller = null;
- if (isFlowCall) {
- func = request.getParameter("FlowCall");
- } else {
- func = getProperty("function", request, mapping);
- controller = getProperty("controller", request, mapping);
- }
+ String func = getProperty("function", request, mapping);
+ String controller = getProperty("controller", request, mapping);
if ("true".equals(FlowConfiguration.getInstance().getProperty("flow.devMode"))) {
SqlMap.reloadConfig();
@@ -150,25 +145,6 @@
throw new ServletException("You must specify a function name to call");
} else {
- // validate JSON in the body of a flowcall
- if (isFlowCall) {
- StringBuffer sb = new StringBuffer();
- char[] buffer = new char[1024];
- int len = 0;
- BufferedReader reader = request.getReader();
- while ((len = reader.read(buffer)) > 0 ) {
- sb.append(buffer, 0, len);
- }
- String json = sb.toString();
- if (log.isDebugEnabled()) {
- log.debug("processing json:"+json);
- }
- if (isValidJSON(json)) {
- context.put("json", json);
- }
-
- }
-
String id = mapping.getProperty("id");
if (id != null) {
args.add(new Interpreter.Argument("id", id));
@@ -176,32 +152,47 @@
// call control script function
- Object ret = interp.callController(controller, func, args, context);
-
- if (isFlowCall) {
- Map atts = ConversionHelper.jsobjectToMap((Scriptable) ret);
- String json = new JSONSerializer().serialize(atts);
- if (log.isDebugEnabled()) {
- log.debug("returning json: "+json);
- }
- response.getWriter().write(json);
- response.getWriter().flush();
- response.getWriter().close();
- return null;
- }
+ interp.callController(controller, func, args, context);
return dispatchToPage(request, response, mapping, forward);
}
} else {
// --- continue an existing flow
- // kick off continuation
- context.put("id", "5");
+ // validate JSON in the body of a flowcall
+ if (isFlowCall) {
+ StringBuffer sb = new StringBuffer();
+ char[] buffer = new char[1024];
+ int len = 0;
+ BufferedReader reader = request.getReader();
+ while ((len = reader.read(buffer)) > 0 ) {
+ sb.append(buffer, 0, len);
+ }
+ String json = sb.toString();
+ if (log.isDebugEnabled()) {
+ log.debug("processing json:"+json);
+ }
+ if (isValidJSON(json)) {
+ context.put("json", json);
+ }
+
+ }
interp.handleContinuation(
request.getParameter("contid"), new LinkedList(), context);
- return dispatchToPage(request, response, mapping, forward);
+ if (isFlowCall) {
+ String json = new JSONSerializer().serialize(forward.getBizData());
+ if (log.isDebugEnabled()) {
+ log.debug("returning json: "+json);
+ }
+ response.getWriter().write(json);
+ response.getWriter().flush();
+ response.getWriter().close();
+ return null;
+ } else {
+ return dispatchToPage(request, response, mapping, forward);
+ }
}
}
Modified: struts/flow/trunk/src/java/org/apache/struts/flow/FlowPlugIn.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/FlowPlugIn.java?rev=371121&r1=371120&r2=371121&view=diff
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/FlowPlugIn.java (original)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/FlowPlugIn.java Sat Jan 21 14:00:28 2006
@@ -239,6 +239,7 @@
classesToRegister.add(SqlMap.class);
interp.initialize(classesToRegister);
interp.register("/org/apache/struts/flow/core/javascript/fom/fom_system.js");
+ interp.register("/org/apache/struts/flow/core/javascript/fom/template.js");
interp.addFlowVariable("struts", new DefaultFlowVariableFactory(Struts.class));
interp.addFlowVariable("params", new DefaultFlowVariableFactory(Params.class));
return interp;
Modified: struts/flow/trunk/src/java/org/apache/struts/flow/Forward.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/Forward.java?rev=371121&r1=371120&r2=371121&view=diff
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/Forward.java (original)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/Forward.java Sat Jan 21 14:00:28 2006
@@ -46,7 +46,7 @@
this.action = (String) setIfNotNull(action, map.get("action"));
this.params = (Map) setIfNotNull(params, map.get("params"));
this.controller = (String) setIfNotNull(controller, map.get("controller"));
- this.redirect = "true".equals(map.get("redirect"));
+ this.redirect = Boolean.TRUE.equals(map.get("redirect"));
this.options = map;
}
Modified: struts/flow/trunk/src/java/org/apache/struts/flow/core/Interpreter.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/Interpreter.java?rev=371121&r1=371120&r2=371121&view=diff
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/Interpreter.java (original)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/Interpreter.java Sat Jan 21 14:00:28 2006
@@ -222,7 +222,7 @@
* @param redirector a <code>Redirector</code> used to call views
* @exception Exception if an error occurs
*/
- void handleContinuation(String continuationId, List params,
+ Object handleContinuation(String continuationId, List params,
WebContext chainCtx)
throws Exception;
}
Modified: struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOM_Flow.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOM_Flow.java?rev=371121&r1=371120&r2=371121&view=diff
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOM_Flow.java (original)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOM_Flow.java Sat Jan 21 14:00:28 2006
@@ -212,7 +212,7 @@
}
public FOM_WebContinuation jsFunction_forward(Object options,
- Object obj,
+ Object bizdata,
Object wk)
throws Exception {
@@ -231,14 +231,26 @@
forward.setUri((String) options);
} else if (options instanceof Scriptable
&& options != Undefined.instance) {
- forward.populate((Map)ConversionHelper.jsobjectToMap((Scriptable)options));
+ Map vals = (Map)ConversionHelper.jsobjectToMap((Scriptable)options);
+ forward.populate(vals);
}
- if (obj != null && obj != Undefined.instance) {
- forward.setBizData((Map)ConversionHelper.jsobjectToMap((Scriptable)obj));
+ if (bizdata != null && bizdata != Undefined.instance) {
+ forward.setBizData((Map)ConversionHelper.jsobjectToMap((Scriptable)bizdata));
}
ctx.put(Constants.FORWARD_KEY, forward);
return fom_wk;
+ }
+
+ public String jsFunction_calculateUri(Object fwd, Object options, String pattern) {
+ Forward forward = (Forward)fwd;
+ if (options instanceof String) {
+ forward.setUri((String) options);
+ } else if (options instanceof Scriptable
+ && options != Undefined.instance) {
+ forward.populate((Map)ConversionHelper.jsobjectToMap((Scriptable)options));
+ }
+ return forward.toUri(pattern);
}
public Scriptable jsFunction_createPageLocal() {
Modified: struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOM_JavaScriptInterpreter.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOM_JavaScriptInterpreter.java?rev=371121&r1=371120&r2=371121&view=diff
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOM_JavaScriptInterpreter.java (original)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOM_JavaScriptInterpreter.java Sat Jan 21 14:00:28 2006
@@ -878,9 +878,10 @@
return ret;
}
- public void handleContinuation(String id, List params,
+ public Object handleContinuation(String id, List params,
WebContext webctx) throws Exception
{
+ Object ret = null;
WebContinuation wk = continuationsMgr.lookupWebContinuation(id, getInterpreterID(), webctx);
if (wk == null) {
@@ -940,7 +941,7 @@
initFlowVariables(kScope.getFlowVariables(), webctx);
Object[] args = new Object[] {k, fom_wk};
try {
- ScriptableObject.callMethod(flow,
+ ret = ScriptableObject.callMethod(flow,
"handleContinuation", args);
} catch (JavaScriptException ex) {
throw locationTracker.getException("Error calling continuation", ex);
@@ -966,6 +967,7 @@
Context.exit();
Thread.currentThread().setContextClassLoader(savedClassLoader);
}
+ return ret;
}
}
Modified: struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/fom_system.js
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/fom_system.js?rev=371121&r1=371120&r2=371121&view=diff
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/fom_system.js (original)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/fom_system.js Sat Jan 21 14:00:28 2006
@@ -30,10 +30,37 @@
}
-FOM_Flow.prototype.wait = function(ttl) {
- this.forward(null, null,
+FOM_Flow.prototype.wait = function(uri, bizData, ttl, fun) {
+ this._wait(uri, bizData, ttl, fun);
+ var funcName = params.FlowCall;
+ if (funcName) {
+ var func = controller[funcName];
+ if (func) {
+ var json;
+ eval("json = "+flow.context.json);
+ var ret = func.call(controller, json);
+ this.forward(null, ret);
+ FOM_Flow.suicide();
+ } else {
+ flow.log.error("Unable to locate function "+funcName+
+ " on controller "+typeof(controller));
+ }
+ } else {
+ flow.log.debug("Not a flow call");
+ }
+}
+
+FOM_Flow.prototype._wait = function(uri, bizData, ttl, fun) {
+ this.forward(uri, bizData,
new FOM_WebContinuation(new Continuation(),
this.continuation, ttl));
+ if (fun) {
+ if (!(fun instanceof Function)) {
+ throw "Expected a function instead of: " + fun;
+ }
+ fun();
+ }
+
FOM_Flow.suicide();
}
@@ -78,6 +105,30 @@
*/
FOM_Flow.prototype.exit = function() {
FOM_Flow.suicide();
+}
+
+
+// This function renders the content directly using Javascript Templates
+FOM_Flow.prototype.renderAndWait = function(page, bizdata, ttl) {
+ var cont = new FOM_WebContinuation(new Continuation(),
+ this.continuation, ttl)
+ bizdata.contid = cont.id;
+ render(page, bizdata);
+ this.forward(null, bizdata, cont);
+ FOM_Flow.suicide();
+}
+
+FOM_Flow.prototype.render = function(page, bizdata) {
+ var res = this.context.response;
+ var stream = struts.servletContext.getResourceAsStream("/WEB-INF/templates/"+page+".jt");
+ if (stream != null) {
+ var text = new String(stream.getText());
+ var html = text.process(bizdata);
+ res.writer.print(html);
+ res.writer.close();
+ } else {
+ res.sendError(res.SC_INTERNAL_SERVER_ERROR, "Unable to find page "+page);
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@struts.apache.org
For additional commands, e-mail: dev-help@struts.apache.org