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]