Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 3D35C200CF6 for ; Mon, 18 Sep 2017 09:09:08 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 3BC231609DB; Mon, 18 Sep 2017 07:09:08 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 592E51609D8 for ; Mon, 18 Sep 2017 09:09:07 +0200 (CEST) Received: (qmail 42930 invoked by uid 500); 18 Sep 2017 07:09:06 -0000 Mailing-List: contact issues-help@struts.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@struts.apache.org Delivered-To: mailing list issues@struts.apache.org Received: (qmail 42920 invoked by uid 99); 18 Sep 2017 07:09:06 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd1-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 18 Sep 2017 07:09:06 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd1-us-west.apache.org (ASF Mail Server at spamd1-us-west.apache.org) with ESMTP id 0647AC5C3B for ; Mon, 18 Sep 2017 07:09:06 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd1-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: -99.202 X-Spam-Level: X-Spam-Status: No, score=-99.202 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, RP_MATCHES_RCVD=-0.001, SPF_PASS=-0.001, USER_IN_WHITELIST=-100] autolearn=disabled Received: from mx1-lw-us.apache.org ([10.40.0.8]) by localhost (spamd1-us-west.apache.org [10.40.0.7]) (amavisd-new, port 10024) with ESMTP id ooUM8hQOo_uP for ; Mon, 18 Sep 2017 07:09:04 +0000 (UTC) Received: from mailrelay1-us-west.apache.org (mailrelay1-us-west.apache.org [209.188.14.139]) by mx1-lw-us.apache.org (ASF Mail Server at mx1-lw-us.apache.org) with ESMTP id 4E6DE610CC for ; Mon, 18 Sep 2017 07:09:03 +0000 (UTC) Received: from jira-lw-us.apache.org (unknown [207.244.88.139]) by mailrelay1-us-west.apache.org (ASF Mail Server at mailrelay1-us-west.apache.org) with ESMTP id 82344E0F23 for ; Mon, 18 Sep 2017 07:09:02 +0000 (UTC) Received: from jira-lw-us.apache.org (localhost [127.0.0.1]) by jira-lw-us.apache.org (ASF Mail Server at jira-lw-us.apache.org) with ESMTP id 9CB822538E for ; Mon, 18 Sep 2017 07:09:00 +0000 (UTC) Date: Mon, 18 Sep 2017 07:09:00 +0000 (UTC) From: "Lukasz Lenart (JIRA)" To: issues@struts.apache.org Message-ID: In-Reply-To: References: Subject: [jira] [Closed] (WW-1330) SAF2 Automatic Ajax Support MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit X-JIRA-FingerPrint: 30527f35849b9dde25b450d4833f0394 archived-at: Mon, 18 Sep 2017 07:09:08 -0000 [ https://issues.apache.org/jira/browse/WW-1330?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ] Lukasz Lenart closed WW-1330. ----------------------------- > SAF2 Automatic Ajax Support > --------------------------- > > Key: WW-1330 > URL: https://issues.apache.org/jira/browse/WW-1330 > 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: code.zip, saf2_auto_ajax.zip > > > Note: Full sample/test app, ready to try in the app server of your choice, can be downloaded here (http://www.omnytex.com/saf2_auto_ajax.zip) > 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: > value > value > value > value value For example: > > Frank > Andrew > Ashley > MCSF > SCJP > > The message may not have elements nested any deeper than that. So, you could not for instance have: > > Andrew > Ashley> > > The presence of the "key" attribute on an element that would otherwise be part of a list, as in the 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: > > > > 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: > > > > > > > > > > > > > > > > > > > > Lastly, you will need to reference the new stack, and/or Result, in your Action mappings: > > > > > 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 was sent by Atlassian JIRA (v6.4.14#64029)