[ https://issues.apache.org/struts/browse/WW-1330?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#action_39706 ]
Frank W. Zammetti commented on WW-1330: --------------------------------------- I just attached the sample app file, since I realized it was no longer on my server at the URL referenced in the first comment. > SAF2 Automatic Ajax Support > --------------------------- > > Key: WW-1330 > URL: https://issues.apache.org/struts/browse/WW-1330 > Project: Struts 2 > Issue Type: New Feature > Components: Actions, Interceptors, Misc > Affects Versions: WW 2.2.2, 2.0.0 > Environment: Any > Reporter: Frank W. Zammetti > Fix For: 2.1.x > > Attachments: code.zip, saf2_auto_ajax.zip > > > Note: Full sample/test app, ready to try in the app server of your choice, > can be downloaded <a href="http://www.omnytex.com/saf2_auto_ajax.zip">here > (http://www.omnytex.com/saf2_auto_ajax.zip)</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", > "certifications":["SCJP","MCSD"]} > 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, i.e.: > 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 is automatically generated by JIRA. - If you think it was sent incorrectly contact one of the administrators: https://issues.apache.org/struts/secure/Administrators.jspa - For more information on JIRA, see: http://www.atlassian.com/software/jira