struts-issues mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Lukasz Lenart (JIRA)" <>
Subject [jira] [Closed] (WW-1330) SAF2 Automatic Ajax Support
Date Mon, 18 Sep 2017 07:09:00 GMT


Lukasz Lenart closed WW-1330.

> SAF2 Automatic Ajax Support
> ---------------------------
>                 Key: WW-1330
>                 URL:
>             Project: Struts 2
>          Issue Type: New Feature
>          Components: Plugin - JSON
>    Affects Versions: WW 2.2.2, 2.0.0
>         Environment: Any
>            Reporter: Frank W. Zammetti
>             Fix For: 2.1.2
>         Attachments:,
> Note: Full sample/test app, ready to try in the app server of your choice, can be downloaded
<a href="">here (</a>
> Strictly speaking, this does not actually have anything specifically to do with AJAX,
but since that is likely to be the most common use case, that's the name I went with.
> Two new Interceptors are introduced, both of which have the goal of automatically populating
the target Action from the incoming request's POST body, either an XML message (AjaxXMLInterceptor)
or a JSON message (AjaxJSONInterceptor).
> In addition, two new Results are introduced, both of which have the goal of automatically
generating a response to the client in the form of an XML message (AjaxXMLResult) or JSON
message (AjaxJSONResult) whether the message is automatically generated from the fields of
the Action.
> Finally, two new marker interfaces are introduced, one to indicate that an Action can
be populated via the AjaxJSONInterceptor (AjaxJSONAware) and one to indicate that the Action
can be populated via the AjaxXMLInterceptor (AjaxXMLAware).
> The Interceptors and Results can of course be mixed and matched, so you can accept a
JSON message, then generate XML to return to the client, or accept a regular request and generate
JSON, or accept XML and generate XML.
> Both of the Interceptors interrogate the incoming request's "content-type" header to
determine if it should do its work.  The AjaxXMLInterceptor looks for the value "text/xml"
and the AjaxJSONInterceptor looks for the value "application/json".
> Incoming JSON
> -----------------------------------------------------
> An incoming JSON message to be used by the AjaxJSONInterceptor must be in the form:
> { "aaa":"bbb", "ccc": [ "ddd", "eee" ], "fff": [ { "ggg":"hhh" } ] }
> For example:
> {
> "firstName" : "Frank", "children" : [ "Ashley", "Andrew" ], "certifications" : [ { "microsoft"
: "MCSD", "sun" : "SCJP" } ] }
> The message may not have any elements nested any deeper than that.  The array section
in this format with the elements "ddd" and "eee" can be used to populate a List or an array.
 The array section in this format with the elements "ggg" and "hhh" will be used to populate
a Map.
> Outgoing JSON
> -----------------------------------------------------
> An outgoing JSON message as generate by the AjaxJSONResult will be in essentially the
same format as above, except that there will be marker values to indicate whether an array
section came from a List or Map.  For example:
> {"physicalAttributes":{"map":{"height":"5.10","weight":"Too much"}},"children":{"list":["Andrew","Ashley"]},"firstName":"Frank",
> Incoming XML
> -----------------------------------------------------
> An incoming XML message to be used by the AjaxXMLInterceptor must be in the form:
>   <scalarName>value</scalarName>
>   <listName>value</listName>
>   <listName>value</listName>
>   <mapName key="keyValue">value</mapName
>   <mapName key="keyValue">value</mapName
> For example:
> <person>
>   <firstName>Frank</firstName>
>   <children>Andrew</children>
>   <children>Ashley</children>
>   <certifications key="microsoft">MCSF</certifications>
>   <certifications key="sun">SCJP</certifications>
> </person>
> The message may not have elements nested any deeper than that.  So, you could not for
instance have:
> <children>
>   <child>Andrew</child>
>   <child>Ashley></child>
> </children>
> The presence of the "key" attribute on an element that would otherwise be part of a list,
as in the <children> elements for example, determines whether the elements become part
of a Map or not.
> Outgoing XML
> -----------------------------------------------------
> An outgoing XML message as generated by the AjaxXMLResult will be in the same form as
the above incoming XML example.
> Incoming Request Content-Type
> -----------------------------------------------------
> In order for either of the Interceptors to do their work, the incoming request must have
the appropriate "content-type" header set.  For JSON, that value is "application/json", and
for XML it's "text/xml".  If the AjaxXMLInterceptor fires for instance, and the "content-type"
is not "text/xml", the interceptor will do nothing.
> Marker Interfaces
> -----------------------------------------------------
> In order for an Action to be populated by one of the interceptors, it must implement
the appropriate marker interface.  These are empty interfaces which serve just to mark an
Action as "aware" of either JSON or XML.  If the AjaxXMLInterceptor fires for instance, and
the Action does not implement the AjaxXMLAware interface, the Action will not be populated.
> Configuration
> -----------------------------------------------------
> To make use of the new Results, you will need to declare them in the package of the Action
mappings that will use them.  The following examples will all show how to set up the XML Result
and Interceptor, but it is the same for the JSON versions, obviously just with XML replaced
with JSON everywhere:
> <result-types>
>   <result-type name="ajaxXML" class="com.opensymphony.webwork.result.AjaxXMLResult"
default="false" />
> </result-types>
> To make use of the Interceptors, you will likewise need to declare them in the package
of the Action mappings that will use them, and you will also need to add them to the Interceptor
stack your Action will use.  One way to do this is to create a new stack.  For example, this
can be accomplished as follows, using the default stack as a model:
> <interceptors>
>   <interceptor name="ajaxXML" class="com.opensymphony.webwork.interceptor.AjaxXMLInterceptor"
>   <interceptor-stack name="xmlStack">
>     <interceptor-ref name="exception" />
>     <interceptor-ref name="alias" />
>     <interceptor-ref name="prepare" />
>     <interceptor-ref name="servlet-config" />
>     <interceptor-ref name="i18n" />
>     <interceptor-ref name="chain" />
>     <interceptor-ref name="model-driven" />
>     <interceptor-ref name="fileUpload" />
>     <interceptor-ref name="static-params" />
>     <interceptor-ref name="params" />
>     <interceptor-ref name="ajaxXML" />
>     <interceptor-ref name="conversionError" />
>     <interceptor-ref name="validation" />
>     <interceptor-ref name="workflow" />
>   </interceptor-stack>
> </interceptors>
> Lastly, you will need to reference the new stack, and/or Result, in your Action mappings:
> <action name="sendXML_receiveXML" class="com.omnytex.TestAction">
>   <interceptor-ref name="xmlStack"/>
>   <result name="success" type="ajaxXML" />
> </action>
> Note that there are other ways to configure all of this, this is just one approach (the
approach used in the sample app).  Please refer to the SAF2 documentation for further details.
> How It All Works
> -----------------------------------------------------
> When either XML or JSON conforming to the above formats is received in the body of a
POST request, and the "content-type" matches the value appropriate for a given Interceptor,
the message is parsed, and a Map of elements is created.  Each element of this Map is either
a List or a Map.  For any element in the XML that does not have the "key" attribute, it will
be part of a List.  For JSON, each array section is examined to determine if it represents
a straight array (or List, same thing in this case) or a Map, and the appropriate type is
inserted into the elements collection.  So, given the above example messages, we would find
that there are three elements in the Map after this XML is parsed: "firstName", "children"
and "certifications". "firstName" and "children" would both of type List (ArrayList specifically)
and "certifications" will be of type Map (HashMap specifically).
> In the Action to be populated, "firstName" would be expected to be a scalar value field,
> private String firstName;
> Naturally, there should be a corresponding mutator for this field.  For the "children"
element, the field could be either a List or a String[] array, either is supported.  For "certifications",
it would need to be a Map.
> Note that in your Action, any arrays must be initialized before this Interceptor fires!
 You can either do this by initializing on the array declaration line, or in a constructor.
 They do not have to be populated, and you can in fact initialize them with a size of 0, (i.e.,
String[] a = new String[0]; is perfectly acceptable).
> Frank W. Zammetti, June 2006

This message was sent by Atlassian JIRA

View raw message