I've been following this thread and solicited you opinions individually.
I thought I might throw out some ideas.
We have developed a struts plug-in that we called rustts (see
attachment). It has properties of a page controller and view
controller.
We implemented the page controller as a subclass of the DispatchAction
class. It combines principles of the DispatchAction and
LookupDispatchAction. This controller handles the creation of various
types of model classes that are associated with visual components thru
our struts extension XML file.
The visual components are defined through xml entries which support
metadata inheritance, much like tiles. Each visual component can be
associated with a model class that controls visual aspects such as if
the visual element should be displayed. The composition of visual
components on a page is defined in the extension XML file. The page XML
element, being the outermost visual element, is associated with the
struts action using a custom attribute in a subclass of ActionMapping.
We have created view helpers that we use within JSP fragments that are
pulled together using tiles. These view helpers use the metadata and
the associated modules to render the view. Currently the helpers make
heavy use of scriptlet. Eventually we could build custom tags that use
the helpers to render the page but we like the ability to quickly change
the skin of a visual element. For example we have a vertical menu and
horizontal menu that use the same helper to render a different
presentation. These view helpers could also be used by velocity to
render a presentation.
We have extended the ActionMapping and ActionForward classes to simplify
propagation of properties in the form bean to the target page.
<action path="/docInfoCategoryEdit" name="DocInfoCategoryForm"
validate="true" input=".dynaPage" scope="request"
parameter="cmd"
type="com.rustts.action.RusttsDispatchAction"
className="com.rustts.action.RusttsActionMapping">
<set-property property="pageId"
value="docInfoCategoryEditPage"/>
<forward name="quit" path="/Welcome.do" redirect="true"
className="com.rustts.action.RusttsForwardAction">
<set-property property="arg6" value="dcDivision" />
</forward>
<!-- next button -->
<forward name="save-success" path="/docInfoType.do"
redirect="false"
className="com.rustts.action.RusttsForwardAction">
<set-property property="arg1" value="drId" />
<set-property property="arg2" value="dcCode" />
<set-property property="arg3" value="drName" />
<set-property property="arg4" value="drComment" />
<set-property property="arg5" value="drOrigIndic" />
<set-property property="arg6" value="dcDivision" />
</forward>
<!-- validate button -->
<forward name="add-success" path="/docInfoCategoryValidate.do"
redirect="false"
className="com.rustts.action.RusttsForwardAction">
<set-property property="arg1" value="drId" />
<set-property property="arg6" value="dcDivision" />
</forward>
</action>
Each model has a public interface that provides callback methods that
can be implemented to conditionally effect different aspects about a
page or how a visual component behaves within a page.
We have extended the TilesRequestProcessor to cache the metadata loaded
by our plugin in request scope making it available to other resources.
Our extension sits on top of struts and is really where we came up with
the name "rustts". Besides being an anagram of Struts, our vision was
that rust�is�a natural byproduct of metal structures of impressive
stature,�and that often rust is most visible where the struts are
joined.
Regards,
Gary
-----Original Message-----
From: Ted Husted [mailto:[EMAIL PROTECTED]
Sent: Sunday, September 28, 2003 8:13 PM
To: Struts Developers List
Subject: Re: Reviving PageController (ViewController?) discussion?
Here's a third idea:
Instead of creating a new class, we could just associate an Action class
with the ActionForward. This is what people do now anyway. It seems to
work, but wastes an ActionMapping and trip through the container.
So, we just add a "type" property to ActionForward, and a step to the
RequestProcessor to handle it when available. If it's there, it gets
called, and the RequestProcess forward to the path. If it's not there,
the RP forwards to the path given by the original forward. Everything
else remains the same.
I'm sure some people will misuse the feature, but some people will
always misuse any feature. At least this way, we recognize what most
people (including me) already do most of the time, put an Action in
front of page. The advantage being that we don't have to waste an
ActionMapping or a trip through the container just to forward to the
page.
-Ted.
Joe Germuska wrote:
> I've been thinking about this a bit; as I see it now, some
> implementation choices might be a little contentious, so I feel like
the
> right approach is discuss first, code later.
>
> Below is my interpretation of the interface based on earlier
discussion
> and such. Note that I suggest we change the name from
"PageController"
> to "ViewController", although I don't feel very strongly about it.
It's
> just one method (plus some javadoc to flesh out the idea.)
>
> package org.apache.struts.action;
>
> public interface ViewController {
>
> /**
> * <p>Perform any view-level preparations after an [EMAIL PROTECTED] Action}
> has executed
> * and before the display is rendered. Return a [EMAIL PROTECTED]
ForwardConfig}
> * instance describing where and how control should be forwarded,
or
> * <code>null</code> if the response has already been
completed.</p>
> *
> * <p>In the simplest case, where an implementation only modifies
the
> * request context, the return value may be the same as the
> <code>ForwardConfig</code>
> * provided as an argument. However, note that implementations
> should not
> * directly modify the given ForwardConfig if it is not the
appropriate
> * return value; they must instead return a new instance
> * (or an implementation-level cached instance).</p>
> *
> * @param forward The ForwardConfig in whose context this
controller is
> * being invoked.
> * @param request The HTTP request we are processing
> * @param response The HTTP response we are creating
> * @return a ForwardConfig which will be used for final view
dispatch,
> * or null if the response has already been completed
> * @exception Exception if the view preparation process throws
> * an exception
> */
> public ForwardConfig prepareView(
> ForwardConfig forward,
> HttpServletRequest request,
> HttpServletResponse response)
> throws Exception;
>
> I think it's important (as noted in the JavaDoc) to help people
> understand that they can't change a "frozen" ForwardConfig. (I kind
of
> feel like this belongs more in something like o.a.s.view, but if it's
> going to be the only class there...)
>
> At 10:31 -0400 9/15/03, Ted Husted wrote:
>
>> I'll post more on this later, but to avoid the "gotcha", I'm now
>> thinking we should try this using a modified
ActionMapping.findForward
>> method. In that way, all the navigational control stays within the
>> Action.
>
>
> The part that seems like it might raise some discussion is about who
> manages the ViewControllers. It seems wasteful to instantiate one
each
> time a view is dispatched. So then does the base ActionMapping cache
> ViewControllers? If that's the case, then what about Module-level
> "global forwards"? You could make a simple support class that wrapped
> up the instantiation and caching and have it available to
ActionMapping
> and implementations of ModuleConfig -- and just to note, the fact that
> ModuleConfig is an interface leaves open the risk that existing
> implementations wouldn't implement the new, more complex contract of
> "findForwardConfig".
>
> I guess I'm waiting to hear more on Ted's teaser; I'm not sure I see
the
> gotcha that makes hiding this behavior in ActionMapping better than
> making it the responsibility of the RequestProcessor, which seems like
a
> clearer place to put a single cache of ViewController classes -- which
> seems to be how Tiles works.
>
> If people want to weigh in on this, I'll distill the discussion into
> draft code once we think the path is clear.
>
> Joe
>
--
Ted Husted,
Junit in Action - <http://www.manning.com/massol/>,
Struts in Action - <http://husted.com/struts/book.html>,
JSP Site Design -
<http://www.amazon.com/exec/obidos/ISBN=1861005512>.
"Get Ready, We're Moving Out!!" - <http://www.clark04.com>
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]
<!--
A "Boolean" represents the result of a binary expression represented
as a true or false value
-->
<!ENTITY % Boolean "(true|false)">
<!--
A "ButtonMethod" represents the method name that will be invoked on the page
model when a page is submitted to an action.
-->
<!ENTITY % ButtonMethod "(add|save|delete|quit|other1|other2)">
<!--
A "button" represents a submit button in a page form. The "page" element aggregates
up to six buttons defined by the "ButtonMethod".
method The type of button
Visible Is the button visible on the page.
MessageKey the message key in the properties file that defines the label on the buton
Validate If the value is true, the action forms validate method will be invoked if
defined (assuming the action validate is also true). If the value is false,
the button is determined to be a struts cancel button and the action forms
validate method is not invoked. A button representing a Quit or Delete action
would not require form validation.
Roles A comma delimited list of security roles that the user must have in order to perform
this action. If the user is not a member of the role(s) the button will not be visible.
-->
<!ELEMENT button (#PCDATA)>
<!ATTLIST button
method %ButtonMethod; #REQUIRED
visible %Boolean; #IMPLIED
messageKey CDATA #IMPLIED
validate %Boolean; #IMPLIED
roles CDATA #IMPLIED
>
<!--
An "Align" represents the horizontal positioning of a column in the a resultset
-->
<!ENTITY % Align "(left|right|center)">
<!--
The "struts-extension" is the root node of the document
-->
<!ELEMENT struts-extension (resultset-definition?, menu-definition?, page-definition?, field-format-pattern-definition?, display-element-definition?, list-element-definition?)>
<!--
A "field-format-pattern-definition" is the container hoding zero or more
"field-format-patterns"
-->
<!ELEMENT field-format-pattern-definition (field-format-pattern*)>
<!--
A "field-format-pattern" represents a common format pattern that should be applied
to a property when converted from a strong type to a string literal or from a string
literal to a strong type. For example, suppose a fiscal ending period (fyEndDt) was
always displayed in the format of MM/dd.
properyName The value of this attribute should correspond to an object property.
pattern The format pattern that corresponds to the Format class designed
for each datatype.
java.text.DecimalFormat;
java.text.MessageFormat;
java.text.SimpleDateFormat;
-->
<!ELEMENT field-format-pattern (#PCDATA)>
<!ATTLIST field-format-pattern
propertyName CDATA #REQUIRED
pattern CDATA #IMPLIED
>
<!--
A "page-definition" is a container node holding zero or more pages
-->
<!ELEMENT page-definition (page*)>
<!--
A "page" element will correspond to a struts action. A set-property tag in the
action, will associating with the pageId in the page node.
A page can contain up to six buttons defined by ButtonMethod. The buttons inherit
characteristics of buttons defined is extended pages.
<action>
<set-property property="rsetId" value="personrs"/>
</action>
pageId Unique identifier for a page
extends The pageId that this page will inherit attributes, menu-items and resultset-items from
titleKey The message resource key used to find the localized page title text
subtitleKey The message resource key of the context description of the page
descriptionKey The message key used to pull text from the resource file describing the page
modelClassname The fully qualified class name of the model that will handle the page’s business logic
transaction This flag indicates if the page will participate in a transaction. If the
value is "true", a synchronization token will be encoded in page links and
the form to ensure that a dirty/cached page is not in use.
focus This will identify the name of the form field that will initially obtain the focus.
-->
<!ELEMENT page (button*, menu-item*, resultset-item*, display-element-item*, list-element-item*)>
<!ATTLIST page
pageId CDATA #REQUIRED
extends CDATA #IMPLIED
titleKey CDATA #IMPLIED
subtitleKey CDATA #IMPLIED
descriptionKey CDATA #IMPLIED
modelClassname CDATA #IMPLIED
transaction %Boolean; #IMPLIED
focus CDATA #IMPLIED
>
<!--
The "resultset-definition" node is a container holding zero or more "resultset" nodes
-->
<!ELEMENT resultset-definition (resultset*)>
<!--
A "resultset" defines a table of rows and columns. One or more resultset can be associated
with a page and a resultset can be associated with many pages.
rsetId Unique identifier for a resultset
messageKey The message key used to form the caption of the resultset
modelClassname The fully qualified java class name of the model that supplies the resultset view
helper with the data source (List of simple beans) and validation logic to
conditionally turn off or on a column link. An instance of this class will be
dynamically loaded by the action class.
rowsPerPage The override/default rows per page that the resultet view helper should render
for a page.
checkboxHeaderKey This is a key into the application properties for the text of the column header fir
the checkbox column
noBoxesCheckedKey This is a key into the properties for when no checkboxes are checked but some are
expected to be
truncatedKey The message key used to display a truncated message in the event all rows could not be delivered
numBoxesProcessedKey The message key that will report on the number of boxes processed
defaultIsSortedAscending The default sort order of the default column
defaultSortByColumn The default column the resultset should first ordered by
extends A parent rsetId the current resultset inherits attributes from
-->
<!ELEMENT resultset (column*)>
<!ATTLIST resultset
rsetId CDATA #REQUIRED
messageKey CDATA #IMPLIED
modelClassname CDATA #IMPLIED
rowsPerPage CDATA #IMPLIED
checkboxHeaderKey CDATA #IMPLIED
noBoxesCheckedKey CDATA #IMPLIED
numBoxesProcessedKey CDATA #IMPLIED
truncatedKey CDATA #IMPLIED
defaultIsSortedAscending %Boolean; #IMPLIED
defaultSortByColumn CDATA #IMPLIED
extends CDATA #IMPLIED
>
<!--
The "column" element represents a single cell of data. It's visual context is supplied from a single
property in a simple java bean. The row of the table is mapped to properties on the bean. Each column
can become a navigational point to another uri.
sequence A numeric sequential identifier used to order the cell from left to right in a table row
propertyName The object attribute name "getter method" of the data source of the column content.
messageKey The message resource key used to find the column header description
length The maximum width in characters a column should be.
The column value will wrap at a word boundary if one can be found.
isSortable A boolean flag indicating the column should allow for sorting.
isLinkable An indicator that the column should become a link to another page.
align The horizontal alignment of a cell value.
sortByColumns This is a value list of property names that will be used to sort a resultset. If this attribute
(sortByColumns) is not specified in the XML column node, the propertyName will be assumed.
The value list can include property names in the mapped value object that are not defined in the resultset.
-->
<!ELEMENT column (link?)>
<!ATTLIST column
sequence CDATA #REQUIRED
propertyName CDATA #REQUIRED
messageKey CDATA #IMPLIED
length CDATA #IMPLIED
isSortable %Boolean; #REQUIRED
isLinkable %Boolean; #REQUIRED
align %Align; #REQUIRED
sortByColumns CDATA #IMPLIED
>
<!--
A "menu-definition" is a container for zero or more menu elements.
-->
<!ELEMENT menu-definition (menu*)>
<!--
A "menu" is a group qualifier for a series of page links. A page can be associated
with one or more menus and one menu can be associated with many pages.
menuId Unique identifier for a menu.
messageKey The resource message key for the text description of link
modelClassname The fully qualified path of a java class that performs the
conditional validation of links within the menu group.
doubleSpace A flag indicating whether or not to double space the menu links
extends a parent menuId the current menu inherits attributes from
-->
<!ELEMENT menu (link*)>
<!ATTLIST menu
menuId CDATA #REQUIRED
messageKey CDATA #REQUIRED
modelClassname CDATA #IMPLIED
doubleSpace %Boolean; #IMPLIED
extends CDATA #IMPLIED
>
<!--
A "menu-item" ties a menu to a page.
menuId unique menu identifier
key arbitrary identifier used by the view to identify a menu as it is associated
with a page.
-->
<!ELEMENT menu-item (#PCDATA)>
<!ATTLIST menu-item
key CDATA #REQUIRED
menuId CDATA #REQUIRED
sequence CDATA #IMPLIED
>
<!--
A "resultset-item" ties a resultset to a page.
rsetId unique resultset identifier
key arbitrary identifier used by the view to identify a resultset as it is
associated with a page.
-->
<!ELEMENT resultset-item (#PCDATA)>
<!ATTLIST resultset-item
key CDATA #REQUIRED
rsetId CDATA #REQUIRED
>
<!--
The "list-element-id" ties a list-element to a page.
key identifier used by the view to identify a list-element entry
listElementId unique list-element identifier
-->
<!ELEMENT list-element-item (#PCDATA)>
<!ATTLIST list-element-item
key CDATA #REQUIRED
listElementId CDATA #REQUIRED
>
<!--
A "link" is a navigational point owned by a column or a menu. A link can define
arguments that should be used to compute the query parameter of the url.
sequence numeric value used to order links, only applies to menu's
messageKey the message resource key used to build the text description of the link
path the uri or url of the target location
isExternal indicates if the location is external to the web application (uri or url)
roles if specified, the user must be authorized to these roles to see this link.
Multiple roles are delimited using a comma (role1, role2)
-->
<!ELEMENT link (arg*)>
<!ATTLIST link
sequence CDATA #IMPLIED
messageKey CDATA #IMPLIED
path CDATA #REQUIRED
isExternal %Boolean; #IMPLIED
roles CDATA #IMPLIED
>
<!--
The value of an "arg" is a object propery name "getter method" of a simple bean
attribute that should be encoded as an argument within the query parameter of the
target uri.
propertyName The name of the property within the formbean or value object
roleName An optional argument name override
-->
<!ELEMENT arg (#PCDATA)>
<!ATTLIST arg
propertyName CDATA #REQUIRED
roleName CDATA #IMPLIED
>
<!--
A "display-element-item" ties a resultset to a page.
displayElementId unique resultset identifier
key arbitrary identifier used by the view to identify a displayElement as it is
associated with a page.
-->
<!ELEMENT display-element-item (#PCDATA)>
<!ATTLIST display-element-item
key CDATA #REQUIRED
displayElementId CDATA #REQUIRED
>
<!ELEMENT display-element-definition (display-element*)>
<!ELEMENT display-element (attribute*, set-property*)>
<!ATTLIST display-element
displayElementId CDATA #REQUIRED
jspName CDATA #IMPLIED
extends CDATA #IMPLIED
modelClassname CDATA #IMPLIED
roles CDATA #IMPLIED
>
<!ELEMENT set-property (#PCDATA)>
<!ATTLIST set-property
name CDATA #REQUIRED
value CDATA #REQUIRED
>
<!ELEMENT attribute (set-property*)>
<!ATTLIST attribute
attName CDATA #IMPLIED
displayName CDATA #IMPLIED
displayElementId CDATA #REQUIRED
requiredField %Boolean; #IMPLIED
sequence CDATA #REQUIRED
roles CDATA #IMPLIED
>
<!--
Container holding list-element entries
-->
<!ELEMENT list-element-definition (list-element*)>
<!--
The list element is a cached collection of values used by the view
to populate an option list. The list is cached using lazy retrieval.
The first time a list is retrieved, it is cached in application scope
within the web container. Subsequent access to the same list will
utilize the cached list.
listElementId unique list identifier
labelProperty The object property name that will be used populate
the text description of an option tag within the homogeneous
collection.
valueProperty The object property name used to populate the value attribute of a
HTML option tag
filterClassname The fully qualified class path to a class implementing the IFilter
interface.
includeBlankElement A flag indicating whether or not to include a blank element at the top of the list
DAOClassname The fully qualified class path to the data access object that
will retrieve a homogeneous collection of value objects. The
DAO class must implement the IDomain interface.
sortByColumns A comma delimited value list of property names within the value
object that list should be sorted on after the filter is applied.
This is similar to the column sortByColumns attribute.
-->
<!ELEMENT list-element (set-property*)>
<!ATTLIST list-element
listElementId CDATA #REQUIRED
labelProperty CDATA #REQUIRED
valueProperty CDATA #REQUIRED
filterClassname CDATA #IMPLIED
includeBlankElement %Boolean; #IMPLIED
DAOClassname CDATA #REQUIRED
sortByColumns CDATA #IMPLIED
>
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]