Dear Wiki user,

You have subscribed to a wiki page or wiki category on "Struts Wiki" for change 
notification.

The following page has been changed by DonBrown:
http://wiki.apache.org/struts/StrutsTi/ControllerMock

The comment on the change is:
Adding controller mock discussion

New page:
#format wiki
#language en
= Controller Mockup =

This design equates Controller=Module/dispatch action modeled after Beehive.  
It features:

 * No configuration - everything done through annotations
 * XDoclet annotations to allow for Java 1.4 as well as, IMO, easier to read 
annotations
 * Option to use Form object or not
 * POJO controller
 * Validation annotations available, even without form object
 * Result forwards not required to be defined, when Action.SUCCESS returned, 
defaults to action name.

The latter two points require some explaination.  First, following the design 
of beehive, validation annotations should be able to be defined in multiple 
places: the controller class, the action method, or the property getter.  Since 
Beehive supports shared flows, perhaps those would be used to share validation 
form definitions among other things.  In this mock, I like the ability to 
define a quick validation properties without bothering with the overhead of a 
full form.  While I'm sure in actual use the annotations would have to support 
more complex definitions, I perfer them to be as minimal as humanly possible.

Second, since most Actions have only one forward, I think it makes sense to 
default a success outcome (encouraging the use of the static field) to a page 
containing the name of the action.  Beehive gives each Controller its own 
prefix path, mapping to a physical path on the hd used when resolving jsp's.  
If the success result is returned and no forward defined, I think the name of 
the action should be provided to the default result type (jsp, velocity, etc) 
to guess the name of the file.  So, for the login action in the "" or default 
Controller, the jsp result type would guess {appRoot}/login.jsp.  This is 
similar to how Ruby on Rails operates.

Finally, I'm warming to how Beehive separates Controller.java files from the 
main source and includes them right along side the jsp's.  I think it would be 
interesting to go farther and, in development mode, leave them there for 
deployment and include a compiling classloader that compiles on file changes.  
Cocoon has already done the legwork for this feature.  This would go a long way 
to encourage rapid development.

== Terminology ==
 * ''page flow controller'': the controller class for a particular module path
 * ''page flow'': an entire "package" of a controller and its associated view 
elements
 
== Why XDoclet? ==

I like XDoclet for the default config/annotation engine for several reasons:
 
 * The annotations are cleaner
 * All annotations will be used at build time to generate an XML file, not 
needed at runtime.  While Java 5 does provide APT, it is a sun-specific tool 
and, at least currently, cannot be invoked by Ant, not to mention it requires 
Java 5 annotations.
 * Since the annotations create an XML file, that is one less XWork 
Configuration implementation to create so we get two config styles for free.  I 
wonder if we could use a properties file to generate XML as well making our job 
that much easier.
 * Able to include in the Struts Ti distro I believe and currently used by many 
Struts developers

That said, I still think we would need to create Java 5 annotations as they 
have several key benefits, not the least being compile-type error checking, but 
I think supporting Java 1.4 is more important personally.

== Controller.java Mockup ==

Following Beehive, this code is located in {approot}/Controller.java.  Since it 
is the default package, it's actions will be called from the root, for example, 
the "index" action will be called by {contextPath}/index.  If it was in the 
'foo' package, it would be located at {approot}/foo/Controller.java and called 
by {contextPath}/foo/index.

{{{

import com.opensymphony.xwork.Action;
import com.opensymphony.xwork.ActionContext;
import java.util.Map;
import com.mycompany.app.UserManager;

public class Controller {

    /** @ti.action */
    public String index() {
        return Action.SUCCESS;
    }    
    
    /** @ti.action */
    public String login() {
        return Action.SUCCESS;
    }

    /**
     * @ti.action
     * @ti.validateRequired userName "User name is required"
     * @ti.validateRequired password "Password is required"
     *
     * @ti.forward name="success" type="redirect" value="index"
     * @ti.forward name="error" type="action" value="login"
     */
    public String processLogin() {
        ActionContext ctx = ActionContext.getContext();
        Map params = ctx.getParameters();
        String userName = (String)params.get("userName");
        String password = (String)params.get("password");

        if (ctx.getMessages().size() == 0 && UserManager.isValid(userName, 
password)) {
            return Action.SUCCESS;
        } else {
            ActionContext.getInstance().put("error", "Invalid login");
            return Action.ERROR;
        }    
    }

    /**
     * Demonstrates login action with POJO form
     * @ti.action
     */
    public String processLoginWithForm(LoginForm form) {
        // do something
        return Action.SUCCESS;
    }
 
    /**
     * POJO form with validation annotations on fields.
     */
    public static final class LoginForm {
        
        private String userName;
        private String password;

        public void setUserName(String name) {
            this.userName = name;
        }

        public void setPassword(String val) {
            this.password = val;
        }

        /**
         * @Ti.validateRequired "User name is required"
         */
        public String getUserName() {
            return this.userName;
        }

        /**
         * @Ti.validateRequired "Password is required"
         */
        public String getPassword() {
            return this.password;
        }
    }    
        
}
}}}

==== Comment by rich on Tue Jul  5 15:08:28 2005 ====
I think that JSR175-style annotations should be prime, rather than the reverse. 
 XDoclet-style annotations are definitely cleaner, but tool support will always 
end up getting built around the standard ones.  Aside from the fact that 
editors will become friendly to raw annotations (which I believe to be true, 
even beyond the current support for statement completion), annotation support 
is already being built into the Eclipse JDT, so higher-level tools (design 
surfaces etc.) will have access to them more easily than they would to XDoclet 
tags.

I'd be happy to have these two goals (XDoclet/JSR175-style annotation support) 
be peers, and in fact, there's a typesystem in Beehive that can run on top of 
both.  I just think it would be a mistake to make tool-friendly annotations a 
secondary goal.

==== Comment by rich on Tue Jul  5 15:19:25 2005 ====
I really like the defaults behavior -- it eliminates a lot of rote code.  One 
comment on this is that returning String is pretty limiting.  It's the approach 
JSF took, and it's a roadblock if you ever want to attach something 
programmatically to the result.  We can wait to see if we have a use for it, 
but we might end up wanting something like Forward in Beehive.  My ideal would 
be to have String and a complex object both be valid return types.

==== Comment by rich on Tue Jul  5 15:20:41 2005 ====
Shouldn't there be an annotation to denote an action?  I think it would be bad 
to have any public String getter turn into a user-addressable action.  
Conversely, I think it would be bad to say that no action can ever start with 
'get'.  Thoughts?

==== Comment by mrdon on Tue Jul  5 16:29:34 2005 ====
I agree both xdoclet and jsr 175 style annotations should be peers.  The one 
feature of jsr 175 annotations that bothered me is you weren't allowed to 
repeat an annotation (i.e. multiple forwards) which forced you to shove 
everything into a giant annotation.  Any way to minimize that?

Regarding return types, I'm not sure how that would work with xwork, but we can 
look into that.  Is there a particular usecase you are thinking of?

Regarding action annotation, good point.  How is this solved in JSF?  I think a 
simple @action marker annotation would do the trick nicely, as much as I hate 
to require a default annotation :/

==== Comment by rich on Tue Jul  5 23:26:39 2005 ====
1) I agree -- the JSR175 restriction on repeating annotations is terrible.  If 
not for that, the annotations actually wouldn't be so bad... just an extra set 
of parentheses.  I don't know of any way to minimize that pain (except through 
a nice hierarchical editor).  I think that people who use editors will stick 
with JSR175, and people who compose and edit by hand will consider XDoclet.  
But, if there are good editors... I bet the former group will dwarf the latter.

2) The main usecase I was thinking of is the mechanism for passing 
initialization data to the view.  Separating this kind of thing out from more 
general means (like request attributes in Servlet land) helps if you want to 
preserve non-long-lived state for 'go-back' situations... returning to a page 
that had validation errors, coming back out of a nested flow, etc.  It also 
helps from the tool angle when there are ways to declare types to go along with 
the actual data that's being passed.  In Beehive there are constructors and 
setters on Forward for passing initializer form beans and "action outputs":

{{{
    return new Forward("success", new LoginForm(...));
}}}
or
{{{
    Forward fwd = new Forward("success");
    fwd.addActionOutput("initData", ...);
    return fwd;
}}}

etc.  There are also optional annotations for declaring the types and 
required/optional flags to go with the actual data.  Assuming this sort of 
thing is useful in Ti (I think it is, but we'll see :) ), we could either 
accept both String and some complex type, or we could decide that {{{return new 
Forward("foo")}}} is simple enough.  In the latter case I think 
Action.SUCCESS|ERROR would still always be a valid return value.

3) In JSF, the method "actions" aren't user-addressable, so they didn't run 
into the same issue.  Components bind to methods through the EL, e.g., 
{{{<h:commandLink action="#{someScope.myBean.login}" .../>}}}, which resolves 
to method login() (not getLogin()... funny muddling of property- and 
method-binding).

==== Comment by mrdon on Wed Jul  6 09:16:01 2005 ====
 1. Well, if we stick to using them for generating xml configuration at 
build-time, then it won't take much extra work to support both.  For instance, 
the code in svn now maps the xdoclet tags into xwork xml, re-using their 
configuration system.
 2. Hmmm...it wouldn't be hard to accept both String and Forward returns, and 
we could stick code between our action invocator and the controller to properly 
process the Forward.  I think the question is if two techniques are more 
confusing to the user.  On one hand, returning String keeps in line with JSF 
and WebWork2, but as you point out, the other adds additional functionality.  
Hmm...
 3. Oh right, requests are for pages and, at least it used to be, everything is 
a POST.  I do prefer requests being for actions, but yes, we will have to 
probably add that marker annotation then.


==== Comment by rich on Wed Jul  6 20:28:42 2005 ====
1) OK, sounds good.  I'd suggest then that we focus first on the runtime, with 
handcoded xwork configs.  We can assume that annotation/tag processing is a 
(large) implementation detail.  It's definitely the part I have the fewest 
questions about.  What do you think?

2. One other thought: if there's always a context available, some of the stuff 
that's done through Forward in Beehive could be done on the context instead.  
Maybe we should start with String and operate under the assumption that the 
context would be used for everything else?

==== Comment by mrdon on Thu Jul  7 09:07:05 2005 ====
 1. Well, actually, I have already written and tested a tag processor and ant 
task that uses xdoclet's xjavadoc and velocity to easily generate the xwork 
config, but you are right we shouldn't focus on it yet.  
 2. Again, already implemented regarding use of Spring. :)  I'm not exactly 
following how that relates to the context, and by context I think you mean 
chain WebContext?

==== Comment by rich on Thu Jul  7 16:23:31 2005 ====
1) Yeah, I saw that.  You've been busy.  :)  I just figured that we'd end up 
stuffing a lot more into the config files than exists now.  The processing 
layer in Beehive is large, because there's so much checking that can be done in 
the annotations and between annotations and types/methods/fields (which is a 
real advantage to annotation processing over XML configuration).

2) I'm confused.  Are we having a String vs. Spring mismatch here?  I just 
meant that we could use whatever context we provide (extension of WebContext?) 
to store what Beehive stores in the Forward object.  So the action methods 
could return Strings.  Instead of Springs.  :)

==== Comment by mrdon on Thu Jul  7 16:34:40 2005 ====
 1. Yep, good point, however, I'm hoping the velocity template will be easy 
enough to edit, but if it starts to absorb too much time, I agree it can wait.
 1. Doh, I read 'Spring'.  I agree returning Strings is a better design than 
Springs :)  Also agree we could move that into a context.  I'm thinking we'll 
need to create a !ControllerContext, much like Struts 1.x's !ActionContext, 
which will wrap xwork's !ActionContext which will have chain's !WebContext.  
Quite the Context party...

==== Comment by rich on Thu Jul  7 17:22:56 2005 ====
2) Yeah... I guess Context parties are the wave of the future.  :)

==== Comment by rich on Thu Jul  7 17:37:11 2005 ====
4) Hey, how would people feel about making the XDoclet-style annotations 
ordered according to hierarchy?  In the current mockup, the @Ti.action 
annotations would come before all the others on a method.  This would allow 
there to be a better correspondance between XDoclet-style and JSR175-style 
annotations (and I don't think it's a harsh requirement).

5) Minor, but it's nice to settle on things like this early: I'd be in favor of 
lowercasing all elements of the annotation names, or uppercasing them all: 
[EMAIL PROTECTED] or [EMAIL PROTECTED]  Are XDoclet tags usually lowercased?  
On the JSR175 side, {{{action}}} is a type (an [EMAIL PROTECTED]), so it would 
seem strange to lowercase it if the wrapper interface ({{{Ti}}}) was uppercased.

==== Comment by mrdon on Thu Jul  7 18:05:37 2005 ====
Both suggestions sound good and pick one - upper-cased tags or lower-cased.

==== Comment by rich on Thu Jul  7 22:08:03 2005 ====
Cool.  :)  I like [EMAIL PROTECTED] because it's easier to type...


==== Comment by rich on Wed Jul 13 16:27:26 2005 ====
I wasn't reflecting hard enough on the Forward-vs-String question.  I think 
we'll need to at least support something like Forward if we want to be able to 
accept dynamically-generated URIs.  Of course we could support String and URI, 
but this seems like a low-flexibility option.

==== Comment by mrdon on Wed Jul 13 16:48:18 2005 ====
Not necessarily - WebWork allows the location attribute (Struts' ActionForword 
'path') to be an OGNL expression.  If we supported pluggable expression 
languages, we could allow the language evaluation engine to process 'location' 
providing dynamic paths.

==== Comment by rich on Sat Jul 16 15:55:16 2005 ====
Say I'm in an action method and I have:
    String url = getSomeURL();

In that case, what do I return in order to forward or redirect to that URL?  
And how do I specify that it's a forward or redirect?

==== Comment by mrdon on Sat Jul 16 16:45:35 2005 ====
I don't know what WebWork2 would suggest, but I'd imagine you'd define two 
forwards, one a dispatch the other a redirect, which pulls the location out of 
the context/request attribute.  While obviously a page like that isn't 
toolable, you can at least tell there will a redirect and a normal dispatch as 
results.

==== Comment by rich on Mon Jul 18 20:40:03 2005 ====
Would the method need to stick the url into a context/request attribute 
directly?  So I understand, could you show what the action method would look 
like?

If we can stick with String, that's good as long as there's not too much arcane 
knowledge required to fit everything in...

==== Comment by mrdon on Tue Jul 19 10:53:16 2005 ====
Yes, you'd have:
{{{
ActionContext.getContext().put("url", url);
}}}
then as your forward:
{{{
@ti.forward name="dynamicUrl" location="/public/#{url}"
}}}
Of course we would use the standard JSP 2.0 EL, but the principle is the same.

==== Comment by rich on Tue Jul 19 13:20:43 2005 ====
Hmm... OK.  I do like that there's an identifiable @ti.forward, although the 
mechanism is difficult to discover.  Much harder than recognizing that there's 
a Forward constructor which takes URI.  I agree with trying this out, though...

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to