|
WAG_RFC_001
Heuristic Request/Response Mappings
THIS IS AN INCOMPLETE, WORKING DRAFT DOCUMENT FOR DISCUSSION PURPOSES ONLY. Heuristic Request/Response MappingsWorking Draft 31 October 2007
This document is currently a working draft published with the intent of soliciting community feedback. Please send your comments and concerns to the public mailing-list: web-alignment-group@googlegroups.com. This document is designed to provide the developer with the information needed to implement the technology within a web development framework for any platform. 1. Overview "Heuristic Request/Response Mappings" is a technology that combines intelligent defaults, convention over configuration, and annotation, in order to streamline web application development. In a Heuristic workflow, the client requests a virtual "action" URI, often via an HTML form, which the system automatically maps to a code-component. (In a Java implementation, for example, a code-component would be a Java class.) The code-component is invoked, and the system selects a view-component (e.g. a JSP template) based on a heuristic that utilizes the original action URI and a result token provided by the code component. Optionally, metadata, in the form of inline annotations or external XML documents, may be used to override the components usually mapped to an action URI or action result. Although, most common application workflows can be realized without resorting to metadata. 1.1. Goals The primary goals of this memorandum is to define the Heuristic Mapping technology in terms of:
A secondary goal is to also define an approach for
1.2. Non-Goals The Heuristic Mappings technology does not address issues such as validation, security, session management, or the generation of a response to common errors, such as "File not found". Developers are expected to utilized existing features found in the underlying platforms to implement such features. 1.3. Limitations TODO 2. Differences From Prior Versions None. 3. Requirements The key words MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, MAY and OPTIONAL in this document are to be interpreted as described in RFC 2119. An implementation is not compliant if it fails to satisfy one or more of the MUST requirements for the patterns it implements. An implementation that satisfies all of the MUST and all of the SHOULD requirements for its features is said to be unconditionally compliant; one that satisfies all of the 'must' requirements but not all of the SHOULD requirements for its features is said to be conditionally compliant. 4. Terminology This section contains a summary of some terminology used elsehwere in this documentation that can help in disambiguation of commonly applied and therefore often overloaded terms. It may help to read other sections
5. Heuristic Action Matching Larger applications tend to the separate the concerns of "business logic" and "display logic" into different components. Often, explicit metadata settings, either in XML or annotation, are used to assemble these concerns back into a single request/response transaction, While the individual metatadata settings are simple, in larger applications the sheer number of settings become both a development and maintenance burden. Heuristic Mappings assemble separted concerns back into a unified workflow without requiring redundant metadata settings. The heuristics are designed to observe conventions that are natural to developers and follow common organization patterns. 5.1. Strategies The heuristics utilize three key matching strategies: Code Suffix, Base Scope, and Method Matching 5.1.1 Code/View Prefix Strategy TODO: This is the most important strategy, and the poorest explanation :) Developers tend to group code-components and view-components into a number of common "scopes". For code-components, these may be "packages" or "namespaces". For view-components, these may be a base folder for page templates. The Code/View Prefix Stategy lets use specify one or more base scopes or prefixes. When matching a component, the heuristic transforms the mapping-prefix into a code-prefix (package name) or a view-prefix (folder name), and prepends the base-code-prefix or base-view-prefix. If the mapping-prefix contains multiple path-segments, then each path-segment is tried in turn, longest to shortest. If there is more than one base-code-prefix or base-view-prefix, each is prepended to the candidate mapping-prefix in turn. 5.1.1. Code Suffix Strategy All of the heuristics are based on the tendency of developers to name related constructs alike. The system provides a code-suffix ("Action") so that a "PersonAction" component can be kept with a related "Person" component. If a "PersonAction" isn't found, the heuristic looks for a "Person" component instead. 5.1.2. Base Scope Strategy Developers often refactor shared functionality into base components. Accordingly, the system can check a more specific location first, and the falls back to check a base location. If a component can be used by more than one workflow, it can be stored in a base location. The base-scope strategy can be applied to both Code Components and View Components. 5.1.3. Method Matching Strategy If a multi-word component isn't found, the system can also check whether the final word is the name of a method. The method-matching strategy gives developers the flexiblity to serve multiple action URIs from a single component. 5.2 Formal Descriptions To describe the heuristics, we provide both an Example section and an Explanation section. To help illustrate both the Example and the Explanation, a BNF-style grammar (BNF-1) is provided, with some common extensions.
5.2.1 Example Given the uri-path
<code-suffix> of "Action", a Java implementation will first try to match "/nested/namespace/my-resource" in the Explicit Mappings. If it is not found, then the implementation will try to match in sequence the candidate-names: 1. MyResourceAction 2. MyResource 3. MyAction.resource() 4. My.resource() 5. my.resource/IndexAction 6. my.resource/Index 7. or utilize the default code-component (!BaseAction) At each step 1-5, for our given URI path, the implementation scans 1. Each <base-code-prefix> plus "nested.namespace." plus <candidate-name> 2. Each <base-code-prefix> plus "nested." plus <candidate-name> 3. Each <base-code-prefix> plus <candidate-name> Given a <base-code-prefix> setting of "actions.", the system will try to match 1. actions.nested.namespace.MyResourceAction 2. actions.nested.namespace.MyResource 3. actions.nested.namespace.MyAction.resource() 4. actions.nested.namespace.My.resource() 5. actions.nested.namespace.my.resource/IndexAction 6. actions.nested.namespace.my.resource/Index and then 7. actions.namespace.MyResourceAction 8. actions.namespace.MyResource 9. actions.namespace.MyAction.resource() 10. actions.namespace.My.resource() 11. actions.namespace.my.resource/IndexAction 12. actions.namespace.my.resource/Index and finally 13. actions.MyResourceAction 14. actions.MyResource 15. actions.MyAction.resource() 16. actions.My.resource() 17. actions.my.resource/IndexAction 18. actions.my.resource/Index returning either the first match found, or the default component (BaseAction). An implementation should add found matches to the Explict Mappings (see Section 7) under the original uri-path ("/nested/namespace/my-resource"), so that a heuristic search does not need to be repeated each time. 5.2.2. Explanation Heuristic Action Matching is applied to the path of a URI, as defined by RFC 3986. In a URI, a path consists of a sequence of path segments separated by a slash ("/") character, which acts as a path-separator. <uri-path> ::= {<path-segment>} | {<path-separator> <path-segment>}The system extracts the final path segment as the "mapping-name". The remainder of the path, if any, is considered the "mapping-path". <uri-path> ::= {<mapping-name>} | {<mapping-prefix> <mapping-name>}The system may apply a series of transformations to the mapping-path. The "to-code-prefix-transformations" may vary by platform. The default transformations for the Java platform are provided as an example. 1 The mapping-path is copied as the candidate-prefix
2 The candidate-prefix is converted to lower case
3 The path-separator-character ("/") is replaced by the code-separator-character (".") On the target platform, the system utilizes the candidate-prefix as a "package" or "namespace" when matching the mapping-name with a code-component-name. The mapping-name may contain one or more semantic "words", separated by a word-separator-character. The default character is the hyphen ("-"). To match the mapping-name to a code-component-name, the system may apply a series of transformations to the mapping-name. The to-code-name-transformations may vary by platform. The default transformations for the Java platform are provided as an example. 1 The mapping-name is copied as the candidate-name
2 The candidate-name is converted to lower case
3 Any letter immediately following any word-separator is converted to upper case
4 The word-separator is removed (spliced) from the candidate-name
5 The initial character is covered to upper case
6 The code-suffix ("Action") is appended to the candidate-nameThe system may provide the base-code-prefix as a list. The implementation must scan each base-code-prefix with-and-without the candidate-prefix. The overall search pattern could be expressed as: <uri-path> ::= {<mapping-name>} | {<mapping-prefix> <mapping-name>}
<candidate-prefix> ::= (to-code-prefix-transformations) <mapping-prefix>
<candidate-name> ::= (to-code-name-transformations) <mapping-name>
<search-prefix> ::= \[<base-code-prefix>*\] \[<candidate-prefix>\]
<search-action> ::= {<search-prefix>} <candidate-name>{<code-suffix>}
<search-method> ::= | {<search-prefix>} <candidate-name-word-1:N>{<code-suffix>}.<candidate-name-word-N> Finally, the search-prefix heuristic is repeated for each path-segment in the candidate-prefix, longest to shortest. For each base-code-prefix (but only until a match is found), the implementation 1 Must scan in each base-code-prefix for a code-component that matches the candidate-prefix prepended to the candidate-name (//my/example/HelloWorldAction). 2 Must truncate the code-suffix ("Action") and try (1) again (/**/my/example/HelloWorld). 3 Should check whether the original identifier contains one or more word-separators, If so, the implementation should truncate the final word of the original identifier, append the code-suffix ("Action"), and scan for a matching component (per (1) and (2)) with a method (function, or procedure) that matches the truncated word (/**/my/example/HelloAction.world). 4 Should repeat (1)-(3) for each path-segment the candidate-prefix (/**/my/HelloWorldAction and /**/HelloworldAction). The no-path-segment case is always tried last, if no other match has been found. 5 Must utilize the default code component and default result type (BaseAction.success) when no other code component is matched. Note that (5) guarantees that each request/response workflow will utilize a code componenet, even if it is a no-op component. The system instantiates the matched code component, and invokes the default method ("success") or an explicit method, if so configured. If no explicit method is specified, and the code component does not contain the default "success" method, no method is invoked, and the default result code ("success") is utilized. 6. Heuristic Result Matching TODO 7. Explicit Mappings TODO 8. Processing a Request/Response TODO 9. Naming Artifacts (URIs, actions, results, parameters) TODO 10. Mapping RESTful URIs to Heuristic URIs. TODO 11. References 12. Bibliography 12.1 REST Bibliography
the Design of Network-based Software Architectures] (Fielding's REST Dissertation)
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||