Converts Core Developers guide into MD
Project: http://git-wip-us.apache.org/repos/asf/struts-site/repo Commit: http://git-wip-us.apache.org/repos/asf/struts-site/commit/f903a0f7 Tree: http://git-wip-us.apache.org/repos/asf/struts-site/tree/f903a0f7 Diff: http://git-wip-us.apache.org/repos/asf/struts-site/diff/f903a0f7 Branch: refs/heads/master Commit: f903a0f749b245b518505fcc6cb28de2e78642e0 Parents: 0d1a56e Author: Lukasz Lenart <[email protected]> Authored: Thu Jun 1 13:33:10 2017 +0200 Committer: Lukasz Lenart <[email protected]> Committed: Thu Jun 1 13:33:10 2017 +0200 ---------------------------------------------------------------------- pom.xml | 4 +- source/_includes/header.html | 2 +- source/_layouts/core-developers.html | 50 ++ ...ssing-application-session-request-objects.md | 134 ++++ source/core-developers/action-chaining.md | 37 + source/core-developers/action-configuration.md | 782 +++++++++++++++++++ source/core-developers/action-event-listener.md | 43 + .../action-mapper-and-action-mapping.md | 46 ++ source/core-developers/action-mapper.md | 374 +++++++++ .../action-proxy-and-actionproxy-factory.md | 81 ++ source/core-developers/after-annotation.md | 38 + .../ajax-client-side-validation.md | 565 ++++++++++++++ source/core-developers/ajax.md | 144 ++++ source/core-developers/alias-interceptor.md | 36 + .../annotation-workflow-interceptor.md | 49 ++ source/core-developers/annotations.md | 116 +++ source/core-developers/application-servers.md | 30 + .../attachments/att1607_overview.png | Bin 0 -> 17554 bytes .../attachments/att1846_nutshell.GIF | Bin 0 -> 18545 bytes .../att2475_Struts2-Architecture.png | Bin 0 -> 66255 bytes .../attachments/struts2-arch.png | Bin 0 -> 19723 bytes source/core-developers/basic-validation.md | 63 ++ source/core-developers/bean-configuration.md | 63 ++ source/core-developers/before-annotation.md | 38 + .../core-developers/before-result-annotation.md | 38 + source/core-developers/chain-result.md | 29 + source/core-developers/chaining-interceptor.md | 134 ++++ source/core-developers/checkbox-interceptor.md | 61 ++ .../core-developers/client-side-validation.md | 88 +++ source/core-developers/client-validation.md | 85 ++ .../conditionalvisitor-validator.md | 30 + .../core-developers/configuration-elements.md | 50 ++ source/core-developers/configuration-files.md | 53 ++ .../configuration-provider-and-configuration.md | 109 +++ .../core-developers/constant-configuration.md | 130 +++ source/core-developers/conversion-annotation.md | 38 + ...nversion-error-field-validator-annotation.md | 36 + .../conversion-error-interceptor.md | 46 ++ source/core-developers/conversion-validator.md | 58 ++ source/core-developers/cookie-interceptor.md | 33 + .../cookie-provider-interceptor.md | 33 + .../create-if-null-annotation.md | 36 + .../create-session-interceptor.md | 36 + .../custom-validator-annotation.md | 40 + .../date-range-field-validator-annotation.md | 36 + source/core-developers/date-validator.md | 35 + source/core-developers/debugging-interceptor.md | 33 + source/core-developers/debugging.md | 121 +++ .../default-workflow-interceptor.md | 41 + source/core-developers/dependency-injection.md | 16 + source/core-developers/development-mode.md | 99 +++ source/core-developers/dispatcher-listener.md | 29 + source/core-developers/dispatcher-result.md | 28 + source/core-developers/dispatcher.md | 36 + .../double-range-field-validator-annotation.md | 36 + source/core-developers/double-validator.md | 35 + source/core-developers/element-annotation.md | 43 + .../email-validator-annotation.md | 36 + source/core-developers/email-validator.md | 35 + .../core-developers/exceptio-configuration.md | 190 +++++ source/core-developers/exception-interceptor.md | 36 + .../execute-and-wait-interceptor.md | 36 + .../expression-validator-annotation.md | 34 + source/core-developers/expression-validator.md | 30 + .../field-expression-validator-annotation.md | 35 + .../fieldexpression-validator.md | 30 + .../core-developers/file-upload-interceptor.md | 83 ++ source/core-developers/file-upload.md | 538 +++++++++++++ .../formatting-dates-and-numbers.md | 111 +++ source/core-developers/freemarker-result.md | 67 ++ source/core-developers/freemarker-support.md | 211 +++++ source/core-developers/glassfish-2-x.md | 23 + source/core-developers/httpheader-result.md | 28 + source/core-developers/https-and-ie-issues.md | 37 + source/core-developers/i18n-interceptor.md | 40 + source/core-developers/include-configuration.md | 43 + source/core-developers/index.md | 66 ++ .../core-developers/input-config-annotation.md | 35 + .../int-range-field-validator-annotation.md | 35 + source/core-developers/int-validator.md | 35 + .../interceptor-configuration.md | 68 ++ source/core-developers/interceptors.md | 380 +++++++++ source/core-developers/jboss-5.md | 44 ++ source/core-developers/key-annotation.md | 36 + .../core-developers/key-property-annotation.md | 36 + source/core-developers/localization.md | 263 +++++++ source/core-developers/logger-interceptor.md | 36 + source/core-developers/logging.md | 145 ++++ .../message-store-interceptor.md | 99 +++ .../core-developers/model-driven-interceptor.md | 66 ++ source/core-developers/model-driven.md | 104 +++ .../core-developers/multiselect-interceptor.md | 36 + source/core-developers/namespace-annotation.md | 30 + .../core-developers/namespace-configuration.md | 142 ++++ source/core-developers/no-op-interceptor.md | 18 + source/core-developers/nutshell.md | 150 ++++ source/core-developers/object-factory.md | 105 +++ source/core-developers/package-configuration.md | 98 +++ .../parameter-filter-interceptor.md | 43 + .../core-developers/parameters-interceptor.md | 228 ++++++ .../parent-package-annotation.md | 28 + source/core-developers/performance-tuning.md | 73 ++ source/core-developers/plaintext-result.md | 28 + source/core-developers/postback-result.md | 30 + source/core-developers/pre-result-listener.md | 55 ++ source/core-developers/prepare-interceptor.md | 41 + .../pure-java-script-client-side-validation.md | 49 ++ .../core-developers/redirect-action-result.md | 57 ++ source/core-developers/redirect-result.md | 51 ++ .../regex-field-validator-annotation.md | 36 + source/core-developers/regex-validator.md | 35 + .../core-developers/reloading-configuration.md | 19 + .../required-field-validator-annotation.md | 35 + .../required-string-validator-annotation.md | 36 + source/core-developers/required-validator.md | 30 + .../core-developers/requiredstring-validator.md | 30 + source/core-developers/restful-action-mapper.md | 184 +++++ source/core-developers/result-annotation.md | 197 +++++ source/core-developers/result-configuration.md | 272 +++++++ source/core-developers/result-types.md | 111 +++ source/core-developers/roles-interceptor.md | 28 + source/core-developers/scope-interceptor.md | 65 ++ .../scoped-model-driven-interceptor.md | 36 + .../servlet-config-interceptor.md | 36 + source/core-developers/short-validator.md | 35 + source/core-developers/static-content.md | 93 +++ .../static-parameters-interceptor.md | 36 + source/core-developers/stream-result.md | 158 ++++ .../string-length-field-validator-annotation.md | 36 + .../core-developers/stringlength-validator.md | 35 + source/core-developers/struts-default-vm.md | 38 + source/core-developers/struts-default-xml.md | 38 + source/core-developers/struts-properties.md | 37 + source/core-developers/struts-xml.md | 30 + source/core-developers/sunone-7-0.md | 53 ++ source/core-developers/timer-interceptor.md | 36 + source/core-developers/token-interceptor.md | 37 + .../token-session-interceptor.md | 36 + .../type-conversion-annotation.md | 36 + source/core-developers/type-conversion.md | 533 +++++++++++++ source/core-developers/unknown-handlers.md | 63 ++ .../core-developers/url-validator-annotation.md | 72 ++ source/core-developers/url-validator.md | 42 + .../core-developers/using-field-validators.md | 34 + .../using-non-field-validators.md | 34 + .../using-visitor-field-validator.md | 34 + source/core-developers/validation-annotation.md | 64 ++ .../core-developers/validation-interceptor.md | 36 + .../validation-parameter-annotation.md | 35 + source/core-developers/validation.md | 390 +++++++++ .../core-developers/validations-annotation.md | 94 +++ source/core-developers/velocity-properties.md | 31 + source/core-developers/velocity-result.md | 28 + .../visitor-field-validator-annotation.md | 36 + source/core-developers/visitor-validator.md | 35 + source/core-developers/web-xml.md | 231 ++++++ source/core-developers/weblogic-6-1.md | 118 +++ source/core-developers/weblogic.md | 121 +++ source/core-developers/websphere.md | 205 +++++ source/core-developers/wildcard-mappings.md | 236 ++++++ source/core-developers/writing-interceptors.md | 102 +++ source/core-developers/xsl-result.md | 168 ++++ source/core-developers/zero-configuration.md | 119 +++ source/security/index.md | 344 ++++++++ 164 files changed, 13885 insertions(+), 3 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/struts-site/blob/f903a0f7/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index 95c8aef..82fe124 100644 --- a/pom.xml +++ b/pom.xml @@ -94,14 +94,14 @@ <argument>-a</argument> <argument>${project.build.directory}/md/attachments</argument> <argument>-o</argument> - <argument>${project.build.directory}/md/getting-started.md</argument> + <argument>${project.build.directory}/md/core-developers.md</argument> <argument>-u</argument> <argument>${confluence.user}:${confluence.password}</argument> <argument>-server</argument> <argument>https://cwiki.apache.org/confluence</argument> <argument>+gfm</argument> <argument>true</argument> - <argument>14811855</argument> + <argument>13945</argument> </arguments> </configuration> </execution> http://git-wip-us.apache.org/repos/asf/struts-site/blob/f903a0f7/source/_includes/header.html ---------------------------------------------------------------------- diff --git a/source/_includes/header.html b/source/_includes/header.html index a55f76d..5dee958 100644 --- a/source/_includes/header.html +++ b/source/_includes/header.html @@ -65,7 +65,7 @@ <li><a href="/docs/tag-reference.html">Tag reference</a></li> <li><a href="http://cwiki.apache.org/S2PLUGINS/home.html">Plugin registry</a></li> <li class="divider"></li> - <li><a href="/getting-started/">Getting Started (WIP)</a></li> + <li><a href="/core-developers/">Core Developers Guide (WIP)</a></li> </ul> </li> <li class="dropdown"> http://git-wip-us.apache.org/repos/asf/struts-site/blob/f903a0f7/source/_layouts/core-developers.html ---------------------------------------------------------------------- diff --git a/source/_layouts/core-developers.html b/source/_layouts/core-developers.html new file mode 100644 index 0000000..090fae5 --- /dev/null +++ b/source/_layouts/core-developers.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"/> + <meta name="viewport" content="width=device-width, initial-scale=1.0"/> + <meta name="Date-Revision-yyyymmdd" content="20140918"/> + <meta http-equiv="Content-Language" content="en"/> + <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> + + <title>{{ page.title }}</title> + + <link href="//fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,400italic,600italic,700italic" rel="stylesheet" type="text/css"> + <link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet"> + <link href="/css/main.css" rel="stylesheet"> + <link href="/css/custom.css" rel="stylesheet"> + <link href="/highlighter/github-theme.css" rel="stylesheet"> + + <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script> + <script type="text/javascript" src="/bootstrap/js/bootstrap.js"></script> + <script type="text/javascript" src="/js/community.js"></script> +</head> +<body> + +{% include header.html %} + +<article class="container"> + <section class="col-md-12"> + <a href="index.html" title="back to Core Developers Guide"><< back to Core Developers Guide</a> + {{ content }} + </section> +</article> + +{% include footer.html %} + +<script> +$(function() { + return $("h2, h3, h4, h5, h6").each(function(i, el) { + var $el, id; + $el = $(el); + id = $el.attr('id'); + if (id) { + $el.removeAttr('id'); + return $el.before($("<a />").addClass('anchor').attr('name', id)); + } + }); +}); +</script> + +</body> +</html> http://git-wip-us.apache.org/repos/asf/struts-site/blob/f903a0f7/source/core-developers/accessing-application-session-request-objects.md ---------------------------------------------------------------------- diff --git a/source/core-developers/accessing-application-session-request-objects.md b/source/core-developers/accessing-application-session-request-objects.md new file mode 100644 index 0000000..b1f192e --- /dev/null +++ b/source/core-developers/accessing-application-session-request-objects.md @@ -0,0 +1,134 @@ +--- +layout: core-developers +title: Accessing application, session, request objects +--- + +# Accessing application, session, request objects + +**DEPRECATED???** + +The framework provides several access helpers to access Session, Application, Request scopes\. + +####Accessing from Java#### + +All the JEE scope attribute maps can be accessed via + +~~~~~~~ +ActionContext +~~~~~~~ +\. + +**Accessing servlet scopes** + + +~~~~~~~ + +Map attr = (Map) ActionContext.getContext().get("attr"); +attr.put("myId",myProp); + +Map application = (Map) ActionContext.getContext().get("application"); +application.put("myId",myProp); + +Map session = (Map) ActionContext.getContext().get("session"); +session.put("myId", myProp); + +Map request = (Map) ActionContext.getContext().get("request"); +request.put("myId",myProp); + +~~~~~~~ + + +> + +> + +> Do not use ActionContext\.getContext() in the constructor of your Action class\. The values may not be set up, and the call may return null for getSession()\. + +> + +We can also access the + +~~~~~~~ +HttpServletRequest +~~~~~~~ + and + +~~~~~~~ +HttpServletResponse +~~~~~~~ + objects themselves through + +~~~~~~~ +ServletActionContext +~~~~~~~ +\. In general this isn't recommended as it will tie our action to the servlet specification\. + +**Setting session attribute through session object** + + +~~~~~~~ + +ServletActionContext.getRequest().getSession().put("myId", myProp); + +~~~~~~~ + +Implementing + +~~~~~~~ +ServletRequestAware +~~~~~~~ + or + +~~~~~~~ +ServletResponseAware +~~~~~~~ +, combined with the + +~~~~~~~ +"servletConfig" interceptor +~~~~~~~ +, is an alternative way to access the request and response objects, with the same caveat\. + +####Accessing from the view (JSP, FreeMarker, etc\.)#### + +Request and session attributes are accessed via OGNL using the + +~~~~~~~ +#session +~~~~~~~ + and + +~~~~~~~ +#request +~~~~~~~ + stack values\. + +The + +~~~~~~~ +#attr +~~~~~~~ + stack value will search the + +~~~~~~~ +javax.servlet.jsp.PageContext +~~~~~~~ + for the specified key\. If the + +~~~~~~~ +PageContext +~~~~~~~ + doean't exist, it will search the request, session, and application scopes, in that order\. + +**Accessing the Session or Request from a JSP** + + +~~~~~~~ + +<s:property value="#session.myId" /> + +<s:property value="#request.myId" /> + +<s:property value="#attr.myId" /> + +~~~~~~~ http://git-wip-us.apache.org/repos/asf/struts-site/blob/f903a0f7/source/core-developers/action-chaining.md ---------------------------------------------------------------------- diff --git a/source/core-developers/action-chaining.md b/source/core-developers/action-chaining.md new file mode 100644 index 0000000..5753419 --- /dev/null +++ b/source/core-developers/action-chaining.md @@ -0,0 +1,37 @@ +--- +layout: core-developers +title: Action Chaining +--- + +# Action Chaining + +The framework provides the ability to chain multiple actions into a defined sequence or workflow\. This feature works by applying a [Chain Result](chain-result.html) to a given Action, and intercepting its target Action's invocation with a [Chaining Interceptor](chaining-interceptor.html)\. + + + +| As a rule, Action Chaining is not recommended\. First explore other options, such as the _Redirect After Post_ technique\. + +| + +####Chain Result#### + +The [Chain Result](chain-result.html) is a result type that invokes an Action with its own Interceptor Stack and Result\. This Interceptor allows an Action to forward requests to a target Action, while propagating the state of the source Action\. Below is an example of how to define this sequence\. + + +~~~~~~~ +{snippet:id=example|lang=xml|javadoc=true|url=com.opensymphony.xwork2.ActionChainResult} +~~~~~~~ + +Another action mapping in the same namespace (or the default "" namespace) can be executed after this action mapping (see [Configuration Files](configuration-files.html)). An optional "namespace" parameter may also be added to specify an action in a different namespace\. + +####Chaining Interceptor#### + +If you need to copy the properties from your previous Actions in the chain to the current action, you should apply the [Chaining Interceptor](chaining-interceptor.html)\. The Interceptor will copy the original parameters from the request, and the ValueStack is passed in to the target Action\. The source Action is remembered by the ValueStack, allowing the target Action to access the properties of the preceding Action(s) using the ValueStack, and also makes these properties available to the final result of the chain, such as the JSP or Velocity page\. + +One common use of Action chaining is to provide lookup lists (like for a dropdown list of states)\. Since these Actions get put on the ValueStack, their properties will be available in the view\. This functionality can also be done using the ActionTag to execute an Action from the display page\. You may also use the [Redirect Action Result](redirect-action-result.html) to accomplish this\. + +#####Use with care##### + +Experience shows that chaining should be used with care\. If chaining is overused, an application can turn into "spaghetti code"\. Actions should be treated as a [Transaction Script](http://martinfowler\.com/eaaCatalog/transactionScript\.html)^[http://martinfowler\.com/eaaCatalog/transactionScript\.html], rather than as methods in a [Business Facade](http://msdn2\.microsoft\.com/en\-us/library/aa291571(VS\.71)\.aspx)^[http://msdn2\.microsoft\.com/en\-us/library/aa291571(VS\.71)\.aspx]\. Be sure to ask yourself why you need to chain from one Action to another\. Is a navigational issue, or could the logic in Action2 be pushed back to a support class or business facade so that Action1 can call it too? + +Ideally, Action classes should be as short as possible\. All the core logic should be pushed back to a support class or a business facade, so that Actions only call methods\. Actions are best used as adapters, rather than as a class where coding logic is defined\. http://git-wip-us.apache.org/repos/asf/struts-site/blob/f903a0f7/source/core-developers/action-configuration.md ---------------------------------------------------------------------- diff --git a/source/core-developers/action-configuration.md b/source/core-developers/action-configuration.md new file mode 100644 index 0000000..995edad --- /dev/null +++ b/source/core-developers/action-configuration.md @@ -0,0 +1,782 @@ +--- +layout: core-developers +title: Action Configuration +--- + +# Action Configuration + +The action mappings are the basic "unit\-of\-work" in the framework\. Essentially, the action maps an identifier to a handler class\. When a request matches the action's name, the framework uses the mapping to determine how to process the request\. + + +#####Action Mappings##### + +The action mapping can specify a set of result types, a set of exception handlers, and an interceptor stack\. Only the + +~~~~~~~ +name +~~~~~~~ + attribute is required\. The other attributes can also be provided at package scope\. + +**A Logon Action** + + +~~~~~~~ +<action name="Logon" class="tutorial.Logon"> + <result type="redirectAction">Menu</result> + <result name="input">/Logon.jsp</result> +</action> +~~~~~~~ + +When using _Convention Plugin_ the action mapping can be configured with annotations: + +**A Logon Action with annotations** + + +~~~~~~~ +package tutorial + +@Action("Logon") // actually that is not necessary as it is added by convention +@Results( + @Result(type="redirectAction", location="Menu"), + @Result(name="input", location="/Logon.jsp") +) +public class Logon { +~~~~~~~ + +#####Action Names##### + +In a web application, the + +~~~~~~~ +name +~~~~~~~ + attribute is matched as part of the location requested by a browser (or other HTTP client)\. The framework will drop the host and application name and the extension and match what's in the middle: the action name\. So, a request for + +~~~~~~~ +http://www.planetstruts.org/struts2-mailreader/Welcome.action +~~~~~~~ + will map to the + +~~~~~~~ +Welcome +~~~~~~~ + action\. + +Within an application a link to an action is usually generated by a Struts Tag\. The tag can specify the action by name, and the framework will render the default extension and anything else that is needed\. Forms may also submit directly to a Struts Action name (rather than a "raw" URI)\. + +**A Hello Form** + + +~~~~~~~ +<s:form action="Hello"> + <s:textfield label="Please enter your name" name="name"/> + <s:submit/> +</s:form> + +~~~~~~~ + +__Action Names With Slashes__ + +If your action names have slashes in them (for example, + +~~~~~~~ +<action name="admin/home" class="tutorial.Admin"/> +~~~~~~~ +) you need to specifically allow slashes in your action names via a constant in the + +~~~~~~~ +struts.xml +~~~~~~~ + file by specifying + +~~~~~~~ +<constant name="struts.enable.SlashesInActionNames" value="true"/> +~~~~~~~ +\. See [JIRA Issue WW\-1383](https://issues\.apache\.org/jira/browse/WW\-1383)^[https://issues\.apache\.org/jira/browse/WW\-1383] for discussion as there are side effects to setting this property to + +~~~~~~~ +true +~~~~~~~ +\. + +__Action Names with Dots and Dashes__ + +Although action naming is pretty flexible, one should pay attention when using dots (eg\. create\.user) and/or dashes (eg\. my\-action)\. While the dot notation has no known side effects at this time, the dash notation will cause problems with the generated JavaScript for certain tags and themes\. Use with caution, and always try to use camelcase action names (eg\. createUser) or underscores (eg\. my\_action)\. + +__Allowed action names__ + + + +~~~~~~~ +DefaultActionMapper +~~~~~~~ + is using pre\-defined RegEx to check if action name matches allowed names\. The default RegEx is defined as follow: + +~~~~~~~ +[a-zA-Z0-9._!/\-]* +~~~~~~~ + \- if at some point this doesn't match your action naming schema you can define your own RegEx and override the default using constant named + +~~~~~~~ +struts.allowed.action.names +~~~~~~~ +, e\.g\.: + + +~~~~~~~ +<struts> + <constant name="struts.allowed.action.names" value="[a-z{}]"*/> + ... +</struts> +~~~~~~~ + +**NOTE**: Please be aware that action names not matching the RegEx will rise an exception\. + +#####Action Methods##### + +The default entry method to the handler class is defined by the Action interface\. + +**Action interface** + + +~~~~~~~ +public interface Action { + public String execute() throws Exception; +} + +~~~~~~~ + +(information) Implementing the Action interface is optional\. If Action is not implemented, the framework will use reflection to look for an + +~~~~~~~ +execute +~~~~~~~ + method\. + +Sometimes, developers like to create more than one entry point to an Action\. For example, in the case of a data\-access Action, a developer might want separate entry\-points for + +~~~~~~~ +create +~~~~~~~ +, + +~~~~~~~ +retrieve +~~~~~~~ +, + +~~~~~~~ +update +~~~~~~~ +, and + +~~~~~~~ +delete +~~~~~~~ +\. A different entry point can be specified by the + +~~~~~~~ +method +~~~~~~~ + attribute\. + + +~~~~~~~ +<action name="delete" class="example.CrudAction" method="delete"> + ... + +~~~~~~~ + + (\!) If there is no + +~~~~~~~ +execute +~~~~~~~ + method and no other method specified in the configuration the framework will throw an exception\. + + + +_Convention Plugin_ allows that by annotating methods: + +**Annotated action method** + + +~~~~~~~ +@Action("crud") +public class CrudAction { + @Action("delete") + public String delete() { + ... + +~~~~~~~ + +#####Wildcard Method##### + +Many times, a set of action mappings will share a common pattern\. For example, all your + +~~~~~~~ +edit +~~~~~~~ + actions might start with the word "edit", and call the + +~~~~~~~ +edit +~~~~~~~ + method on the Action class\. The + +~~~~~~~ +delete +~~~~~~~ + actions might use the same pattern, but call the + +~~~~~~~ +delete +~~~~~~~ + method instead\. + +Rather than code a separate mapping for each action class that uses this pattern, you can write it once as a wildcard mapping\. + + +~~~~~~~ +<action name="*Crud" class="example.Crud" method="{1}"> + ... + +~~~~~~~ + +Here, a reference to "editCrud" will call the + +~~~~~~~ +edit +~~~~~~~ + method on an instance of the Crud Action class\. Likewise, a reference to "deleteCrud" will call the + +~~~~~~~ +delete +~~~~~~~ + method instead\. + +Another common approach is to postfix the method name and set it off with an exclamation point (aka "bang"), underscore, or other special character\. + ++ "action=Crud\_input" + ++ "action=Crud\_delete" + +To use a postfix wildcard, just move the asterisk and add an underscore\. + + +~~~~~~~ +<action name="Crud_*" class="example.Crud" method="{1}"> + +~~~~~~~ + +From the framework's perspective, a wildcard mapping creates a new "virtual" mapping with all the same attributes as a conventional, static mapping\. As a result, you can use the expanded wildcard name as the name of validation, type conversion, and message resource files, just as if it were an Action name (which it is\!)\. + ++ + +~~~~~~~ +Crud_input-validation.xml +~~~~~~~ + ++ + +~~~~~~~ +Crud_delete-conversion.xml +~~~~~~~ + +If Wildcard Method mapping uses a "\!" in the action name, the Wildcard Method will overlap with another flexible approach to mapping, \. To use action names that include the "\!" character, set struts\.enable\.DynamicMethodInvocation to FALSE in the application configuration\. + +> + +#####Dynamic Method Invocation##### + +There's a feature embedded in Struts 2 that lets the "\!" (bang) character invoke a method other than + +~~~~~~~ +execute +~~~~~~~ +\. It is called "Dynamic Method Invocation" aka DMI\. + +DMI will use the string following a "\!" character in an action name as the name of a method to invoke (instead of + +~~~~~~~ +execute +~~~~~~~ +)\. A reference to " + +~~~~~~~ +Category!create.action +~~~~~~~ +", says to use the "Category" action mapping, but call the + +~~~~~~~ +create +~~~~~~~ + method instead\. + +Another way to use DMI is to provide HTTP parameters prefixed with " + +~~~~~~~ +method: +~~~~~~~ +"\. For example in the URL it could be " + +~~~~~~~ +Category.action?method:create=foo +~~~~~~~ +", the parameter value is ignored\. In POST\-Requests that can be used e\.g\. with a hidden parameter ( + +~~~~~~~ +<s:hidden name="method:create" value="foo" /> +~~~~~~~ +) or along with a button ( + +~~~~~~~ +<s:submit method="create" /> +~~~~~~~ +)\. + +For Struts 2, we added a switch to disable DMI for two reasons\. First, DMI can cause security issues if POJO actions are used\. Second, DMI overlaps with the Wildcard Method feature that we brought over from Struts 1 (and from Cocoon before that)\. If you have security concerns, or would like to use the "\!" character with Wildcard Method actions, then set + +~~~~~~~ +struts.enable.DynamicMethodInvocation +~~~~~~~ + to + +~~~~~~~ +FALSE +~~~~~~~ + in the application configuration\. + +The framework does support DMI, but there are problems with way DMI is implemented\. Essentially, the code scans the action name for a "\!" character, and finding one, tricks the framework into invoking the other method instead of + +~~~~~~~ +execute +~~~~~~~ +\. The other method is invoked, but it uses the same configuration as the + +~~~~~~~ +execute +~~~~~~~ + method, including validations\. The framework "believes" it is invoking the + +~~~~~~~ +Category +~~~~~~~ + action with the + +~~~~~~~ +execute +~~~~~~~ + method\. + +The Wildcard Method feature is implemented differently\. When a Wildcard Method action is invoked, the framework acts as if the matching action had been hardcoded in the configuration\. The framework "believes" it's executing the action + +~~~~~~~ +Category!create +~~~~~~~ + and "knows" it is executing the + +~~~~~~~ +create +~~~~~~~ + method of the corresponding Action class\. Accordingly, we can add for a Wildcard Method action mapping its own validations, message resources, and type converters, just like a conventional action mapping\. For this reason, the is preferred\. + +__Strict DMI__ + +In Struts 2\.3, an option was added to restrict the methods that DMI can invoke\. First, set the attribute + +~~~~~~~ +strict-method-invocation="true" +~~~~~~~ + on your + +~~~~~~~ +<package> +~~~~~~~ + element\. This tells Struts to reject any method that is not explicitly allowed via either the + +~~~~~~~ +method +~~~~~~~ + attribute (including wildcards) or the + +~~~~~~~ +<allowed-methods> +~~~~~~~ + tag\. Then specify + +~~~~~~~ +<allowed-methods> +~~~~~~~ + as a comma\-separated list of method names in your + +~~~~~~~ +<action> +~~~~~~~ +\. (If you specify a + +~~~~~~~ +method +~~~~~~~ + attribute for your action, you do not need to list it in + +~~~~~~~ +<allowed-methods> +~~~~~~~ +\.) + +Note that you can specify + +~~~~~~~ +<allowed-methods> +~~~~~~~ + even without + +~~~~~~~ +strict-method-invocation +~~~~~~~ +\. This restricts access only for the specific actions that have + +~~~~~~~ +<allowed-methods> +~~~~~~~ +\. + +**Example struts\.xml** + + +~~~~~~~ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE struts PUBLIC + "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" + "http://struts.apache.org/dtds/struts-2.3.dtd"> +<struts> + + <constant name="struts.enable.DynamicMethodInvocation" value="true"/> + + <package name="default" extends="struts-default" strict-method-invocation="true"> + + <action name="index" class="org.apache.struts2.examples.actions.Index"> + <result name="success" type="redirectAction">hello</result> + </action> + + <action name="hello" class="org.apache.struts2.examples.actions.HelloAction"> + <result name="success">/WEB-INF/content/hello.jsp</result> + <result name="redisplay" type="redirectAction">hello</result> + <allowed-methods>add</allowed-methods> + </action> + + </package> +</struts> + +~~~~~~~ + +__Strict Method Invocation__ + +In Struts 2\.5 the Strict DMI was extended and it's called **Strict Method Invocation** aka SMI\. You can imagine that the DMI is a "border police", where SMI is a "tax police" and keeps eye on internals\. With this version, SMI is enabled by default ( + +~~~~~~~ +strict-method-invocation +~~~~~~~ + attribute is set to + +~~~~~~~ +true +~~~~~~~ + by default in + +~~~~~~~ +struts-default +~~~~~~~ + package), you have option to disable it per package \- there is no global switch to disable SMI for the whole application\. To gain advantage of new configuration option please use the latest DTD definition: + +**Struts 2\.5 DTD** + + +~~~~~~~ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE struts PUBLIC + "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN" + "http://struts.apache.org/dtds/struts-2.5.dtd"> +<struts> +... +</struts> +~~~~~~~ + +SMI works in the following way: + ++ + +~~~~~~~ +<allowed-methods> +~~~~~~~ + / + +~~~~~~~ +@AllowedMethods +~~~~~~~ + is defined per action \- SMI works without switching it on but just for those actions (plus adding + +~~~~~~~ +<global-allowed-methods/> +~~~~~~~ +) + ++ SMI is enabled but no + +~~~~~~~ +<allowed-methods> +~~~~~~~ + / + +~~~~~~~ +@AllowedMethods +~~~~~~~ + are defined \- SMI works but only with + +~~~~~~~ +<global-allowed-methods/> +~~~~~~~ + ++ SMI is disabled \- call to any action method is allowed that matches the default RegEx \- + +~~~~~~~ +([A-Za-z0-9_$]*) +~~~~~~~ + +You can redefine the default RegEx by using a constant as follow + +~~~~~~~ +<constant name="struts.strictMethodInvocation.methodRegex" value="([a-zA-Z]*)"/> +~~~~~~~ + + +When using wildcard mapping in actions' definitions SMI works in two ways: + +| + +| + +| + SMI is disabled \- any wildcard will be substituted with the default RegEx, ie\.: \<action name="Person\*" method="perform\*"\> will be translated into allowedMethod = "regex:perform(\[A\-Za\-z0\-9\_\$\]\*)"\. + +| + +| + SMI is enabled \- no wildcard substitution will happen, you must strictly define which methods can be accessed by annotations or \<allowed\-method/\> tag\. + +| + +| + +You can configure SMI per + +~~~~~~~ +<action/> +~~~~~~~ + using + +~~~~~~~ +<allowed-methods/> +~~~~~~~ + tag or via + +~~~~~~~ +@AllowedMethod +~~~~~~~ + annotation plus using per + +~~~~~~~ +<package/> +~~~~~~~ + + +~~~~~~~ +<global-allowed-methods/> +~~~~~~~ +, see the examples below: + +**SMI via struts\.xml** + + +~~~~~~~ +<package ...> + ... + <global-allowed-methods>execute,input,back,cancel,browse</global-allowed-methods> + ... + + <action name="Bar"> + <allowed-methods>foo,bar</allowed-methods> + </action> + + ... +</package> + + +~~~~~~~ + +**SMI via annotation on action class level** + + +~~~~~~~ +@AllowedMethods("end") +public class ClassLevelAllowedMethodsAction { + public String execute() { + return ... + } +} +~~~~~~~ + +**SMI via annotation on package level (in package\-info\.java)** + + +~~~~~~~ [email protected]({"home", "start"}) +package org.apache.struts2.convention.actions.allowedmethods; +~~~~~~~ + +Allowed methods can be defined as: + ++ literals ie\. in xml: + +~~~~~~~ +execute,cancel +~~~~~~~ +or in annotation: + +~~~~~~~ + {"execute", "cancel"} +~~~~~~~ + ++ patterns when using with wildcard mapping, i\.e + +~~~~~~~ +<action ... method="do{2}"/> +~~~~~~~ + ++ RegExs using + +~~~~~~~ +regex: +~~~~~~~ + prefix, ie: + +~~~~~~~ +<global-allowed-methods>execute,input,cancel,regex:user([A-Z]*)</global-allowed-methods> +~~~~~~~ + + +Please be aware when using your own Configurationprovider that the logic to set allowed methods is defined in built\-in providers \- XmlConfigurationProvider and PackageBasedActionConfigBuilder \- and you must replicate such logic in your code as by default only execute method is allowed, even when SMI is disabled\. + +| + +#####ActionSupport Default##### + +If the class attribute in an action mapping is left blank, the + +~~~~~~~ +com.opensymphony.xwork2.ActionSupport +~~~~~~~ + class is used as a default\. + + +~~~~~~~ +<action name="Hello"> + // ... +</action> + +~~~~~~~ + +(information) The ActionSupport class has an + +~~~~~~~ +execute +~~~~~~~ + method that returns "success" and an + +~~~~~~~ +input +~~~~~~~ + method that returns "input"\. +(information) To specify a different class as the default Action class, set the + +~~~~~~~ +default-class-ref +~~~~~~~ + package attribute\. + +(light\-on) For more about using wildcards, see [Wildcard Mappings](wildcard-mappings.html)\. + +#####Post\-Back Default##### + +A good practice is to link to actions rather than pages\. Linking to actions encapsulates which server page renders, and ensures that an Action class can fire before a page renders\. + +Another common workflow stategy is to first render a page using an alternate method, like + +~~~~~~~ +input +~~~~~~~ + and then have it submit back to the default + +~~~~~~~ +execute +~~~~~~~ + method\. + +Using these two strategies together creates an opportunity to use a "post\-back" form that doesn't specify an action\. The form simply submits back to the action that created it\. + +**Posting Back** + + +~~~~~~~ +<s:form> + <s:textfield label="Please enter your name" name="name"/> + <s:submit/> +</s:form> + +~~~~~~~ + +#####Action Default##### + +Usually, if an action is requested, and the framework can't map the request to an action name, the result will be the usual "404 \- Page not found" error\. But, if you would prefer that an omnibus action handle any unmatched requests, you can specify a default action\. If no other action matches, the default action is used instead\. + + +~~~~~~~ +<package name="Hello" extends="action-default"> + + <default-action-ref name="UnderConstruction"/> + + <action name="UnderConstruction"> + <result>/UnderConstruction.jsp</result> + </action> + + ... + +~~~~~~~ + +There are no special requirements for the default action\. Each package can have its own default action, but there should only be one default action per namespace\. + +**(\!) One to a Namespace** + + +> + +> + +> The default action features should be set up so that there is only one default action per namespace\. If you have multiple packages declaring a default action with the same namespace, there is no guarantee which action will be the default\. + +> + +__Wildcard Default__ + +Using wildcards is another approach to default actions\. A wildcard action at the end of the configuration can be used to catch unmatched references\. + + +~~~~~~~ +<action name="*"> + <result>/{1}.jsp</result> +</action> + +~~~~~~~ + +When a new action is needed, just add a stub page\. + + (\!) It's important to put a "catchall" wildcard mapping like this at the end of your configuration so it won't attempt to map every request\! http://git-wip-us.apache.org/repos/asf/struts-site/blob/f903a0f7/source/core-developers/action-event-listener.md ---------------------------------------------------------------------- diff --git a/source/core-developers/action-event-listener.md b/source/core-developers/action-event-listener.md new file mode 100644 index 0000000..3b45b16 --- /dev/null +++ b/source/core-developers/action-event-listener.md @@ -0,0 +1,43 @@ +--- +layout: core-developers +title: ActionEventListener +--- + +# ActionEventListener + +A ActionEventListener can be used to add some logic when action's instance is created or when exception occurred during processing the action\. + +#####Configuration##### + +Right now there can be only one instance of ActionEventListener for the whole Struts2 application\. You can configure it via struts\.xml by adding bean definition: + + +~~~~~~~ + +<bean type="com.opensymphony.xwork2.ActionEventListener" class="com.demo.MyActionEventListener"/> + +~~~~~~~ + +#####Examples##### + + + +~~~~~~~ + +public class MyActionEventListener implements ActionEventListener { + + public Object prepare(Object action, ValueStack stack) { + if (action instanceof MyBaseAction) { + ((MyBaseAction)action).setUserService(stack.findValue("userService")); + } + } + + public String handleException(Throwable t, ValueStack stack) { + if (t instanceof MyBusinessException) { + return stack.findString("defaultBusinesResult"); + } + return null; + } +} + +~~~~~~~ http://git-wip-us.apache.org/repos/asf/struts-site/blob/f903a0f7/source/core-developers/action-mapper-and-action-mapping.md ---------------------------------------------------------------------- diff --git a/source/core-developers/action-mapper-and-action-mapping.md b/source/core-developers/action-mapper-and-action-mapping.md new file mode 100644 index 0000000..4fea296 --- /dev/null +++ b/source/core-developers/action-mapper-and-action-mapping.md @@ -0,0 +1,46 @@ +--- +layout: core-developers +title: Action Mapper & Action Mapping +--- + +# Action Mapper & Action Mapping + +The ActionMapper fetches the ActionMapping object corresponding to a given request\. Essentially, the ActionMapping is a data transfer object that collects together details such as the Action class and method to execute\. The mapping is utilized by the Dispatcher and various user interface components\. It is customizable through + +~~~~~~~ +struts.mapper.class +~~~~~~~ + entry in + +~~~~~~~ +struts.properties +~~~~~~~ +\. + +####Customize#### + +Custom ActionMapper must implement ActionMapper interface and have a default constructor\. + +> + +~~~~~~~ + +struts.mapper.class=foo.bar.MyCustomActionMapper + +~~~~~~~ + + +~~~~~~~ + +public class MyCustomActionMapper implements ActionMapper { + public ActionMapping getMapping(HttpServletRequest request, + ConfigurationManager configManager) { + .... + } + + public String getUriFromActionMapping(ActionMapping mapping) { + .... + } +} + +~~~~~~~ http://git-wip-us.apache.org/repos/asf/struts-site/blob/f903a0f7/source/core-developers/action-mapper.md ---------------------------------------------------------------------- diff --git a/source/core-developers/action-mapper.md b/source/core-developers/action-mapper.md new file mode 100644 index 0000000..b4ffc20 --- /dev/null +++ b/source/core-developers/action-mapper.md @@ -0,0 +1,374 @@ +--- +layout: core-developers +title: ActionMapper +--- + +# ActionMapper + + +####Description#### + +The ActionMapper interface provides a mapping between HTTP requests and action invocation requests and vice\-versa\. + +When given an HttpServletRequest, the ActionMapper may return null if no action invocation request matches or it may return an \{\{ActionMapping\}\} that describes an action invocation for the framework to try\. + +The ActionMapper is not required to guarantee that the \{\{ActionMapping\}\} returned be a real action or otherwise ensure a valid request\. Accordingly, most ActionMappers do not need to consult the Struts configuration just to determine if a request should be mapped\. + +Just as requests can be mapped from HTTP to an action invocation, the opposite is true as well\. However, because HTTP requests (when shown in HTTP responses) must be in String form, a String is returned rather than an actual request object\. + +####DefaultActionMapper#### + +Default action mapper implementation, using the standard + +~~~~~~~ +*.[ext] +~~~~~~~ + (where ext usually **action**) pattern\. The extension is looked up from the Struts configuration key \{\<b\>\{struts\.action\.extension\}\}\. + +To help with dealing with buttons and other related requirements, this mapper (and other \{\{ActionMapper\}\}s, we hope) has the ability to name a button with some predefined prefix and have that button name alter the execution behaviour\. The four prefixes are: + ++ Method prefix \- + +~~~~~~~ +method:default +~~~~~~~ + ++ Action prefix \- + +~~~~~~~ +action:dashboard +~~~~~~~ + +In addition to these four prefixes, this mapper also understands the action naming pattern of + +~~~~~~~ +foo!bar +~~~~~~~ + in either the extension form (eg: + +~~~~~~~ +foo!bar.action +~~~~~~~ +) or in the prefix form (eg: + +~~~~~~~ +action:foo!bar +~~~~~~~ +)\. This syntax tells this mapper to map to the action named + +~~~~~~~ +foo +~~~~~~~ + and the method + +~~~~~~~ +bar +~~~~~~~ +\. + +#####Method prefix##### + +With method\-prefix, instead of calling baz action's + +~~~~~~~ +execute() +~~~~~~~ + method (by default if it isn't overridden in + +~~~~~~~ +struts.xml +~~~~~~~ + to be something else), the baz action's + +~~~~~~~ +anotherMethod() +~~~~~~~ + will be called\. A very elegant way determine which button is clicked\. Alternatively, one would have submit button set a particular value on the action when clicked, and the + +~~~~~~~ +execute() +~~~~~~~ + method decides on what to do with the setted value depending on which button is clicked\. + + +~~~~~~~ +<!-- START SNIPPET: method-example --> +<s:form action="baz"> + <s:textfield label="Enter your name" name="person.name"/> + <s:submit value="Create person"/> + <s:submit method="anotherMethod" value="Cancel"/> +</s:form> +<!-- END SNIPPET: method-example --> +~~~~~~~ + +#####Action prefix##### + +With action\-prefix, instead of executing baz action's + +~~~~~~~ +execute() +~~~~~~~ + method (by default if it isn't overridden in struts\.xml to be something else), the anotherAction action's + +~~~~~~~ +execute() +~~~~~~~ + method (assuming again if it isn't overridden with something else in + +~~~~~~~ +struts.xml +~~~~~~~ +) will be executed\. + + +~~~~~~~ +<!-- START SNIPPET: action-example --> +<s:form action="baz"> + <s:textfield label="Enter your name" name="person.name"/> + <s:submit value="Create person"/> + <s:submit action="anotherAction" value="Cancel"/> +</s:form> +<!-- END SNIPPET: action-example --> +~~~~~~~ + +#####Allowed action name RegEx##### + +By default the mapper will check if extracted action name matches provided RegEx, i\.e\. + +~~~~~~~ +[a-zA-Z0-9._!/\-]* +~~~~~~~ +\. You redefine this RegEx by defining a constant in + +~~~~~~~ +struts.xml +~~~~~~~ + named + +~~~~~~~ +struts.allowed.action.names +~~~~~~~ +\. If action name doesn't match the RegEx a default action name will be returned which is defined as + +~~~~~~~ +index +~~~~~~~ +\. You can also redefine this by specifying constant + +~~~~~~~ +struts.default.action.name +~~~~~~~ + in + +~~~~~~~ +struts.xml +~~~~~~~ + +#####Allowed method name RegEx##### + +The same logic as above is used for extracted methods, the default RegEx + +~~~~~~~ +( +~~~~~~~ + + +~~~~~~~ +[a-zA-Z_]*[0-9]*) +~~~~~~~ + is used to check if method is allowed, you can change this by setting constant + +~~~~~~~ +struts.allowed.method.names +~~~~~~~ + in + +~~~~~~~ +struts.xml +~~~~~~~ +\. If method doesn't match the RegEx a default method is returned, i\.e\. + +~~~~~~~ +execute +~~~~~~~ +\. This can be changed by defining constant + +~~~~~~~ +struts.default.method.name +~~~~~~~ + in + +~~~~~~~ +struts.xml +~~~~~~~ +\. + +Please note that this funcionallity only works when [Dynamic Method Invocation](https://cwiki\.apache\.org/confluence/display/WW/Action\+Configuration\#ActionConfiguration\-DynamicMethodInvocation)^[https://cwiki\.apache\.org/confluence/display/WW/Action\+Configuration\#ActionConfiguration\-DynamicMethodInvocation] is enabled\. + +####Custom ActionMapper#### + +You can define your own ActionMapper by implementing + +~~~~~~~ +org.apache.struts2.dispatcher.mapper.ActionMapper +~~~~~~~ + then configuring Struts 2 to use the new class in struts\.xml + + +~~~~~~~ +<bean type="org.apache.struts2.dispatcher.mapper.ActionMapper" name="mymapper" class="com.mycompany.myapp.MyActionMapper" /> +<constant name="struts.mapper.class" value="mymapper" /> + +~~~~~~~ + +Possible uses of the ActionMapper include defining your own, cleaner namespaces, such as URLs like **/person/1**, which would be similar to a request to **/getPerson\.action?personID=1** using the DefaultActionMapper\. + +####CompositeActionMapper#### + +A composite action mapper that is capable of delegating to a series of if the former failed to obtained a valid _ActionMapping_ or uri\. + +It is configured through [struts.xml](struts-xml.html)\. For example, with the following entries in struts\.xml + + +~~~~~~~ +<constant name="struts.mapper.class" value="composite" /> +<constant name="struts.mapper.composite" value="struts,restful,restful2" /> + +~~~~~~~ + +When + +~~~~~~~ +CompositeActionMapper#getMapping(HttpServletRequest, ConfigurationManager) +~~~~~~~ + or + +~~~~~~~ +CompositeActionMapper#getUriFromActionMapping(ActionMapping) +~~~~~~~ + is invoked, + +~~~~~~~ +CompositeActionMapper +~~~~~~~ + would go through these s in sequence starting from identified by + +~~~~~~~ +struts.mapper.composite.1 +~~~~~~~ +, followed by + +~~~~~~~ +struts.mapper.composite.2 +~~~~~~~ + and finally + +~~~~~~~ +struts.mapper.composite.3 +~~~~~~~ + (in this case) until either one of the return a valid result (not null) or it runs out of in which case it will just return null for both + +~~~~~~~ +CompositeActionMapper#getMapping(HttpServletRequest, ConfigurationManager) +~~~~~~~ + and + +~~~~~~~ +CompositeActionMapper#getUriFromActionMapping(ActionMapping) +~~~~~~~ + methods\. + +For example with the following in struts\.xml: + + +~~~~~~~ +<constant name="struts.mapper.class" value="composite" /> +<constant name="struts.mapper.composite" value="struts,restful" /> + +~~~~~~~ + + + +~~~~~~~ +CompositeActionMapper +~~~~~~~ + will be configured with 2 ActionMapper, namely "struts" which is + +~~~~~~~ +org.apache.struts2.dispatcher.mapper.DefaultActionMapper +~~~~~~~ + and "restful" which is + +~~~~~~~ +org.apache.struts2.dispatcher.mapper.RestfulActionMapperRestfulActionMapper +~~~~~~~ +\. + +~~~~~~~ +CompositeActionMapper +~~~~~~~ + would consult each of them in order described above\. + +####PrefixBasedActionMapper#### + + + +~~~~~~~ +{snippet:id=description|javadoc=true|url=org.apache.struts2.dispatcher.mapper.PrefixBasedActionMapper} +~~~~~~~ + +__PrefixBasedActionProxyFactory__ + + + +~~~~~~~ +{snippet:id=description|javadoc=true|url=org.apache.struts2.factory.PrefixBasedActionProxyFactory} +~~~~~~~ + +####ActionMapper and ActionMapping objects#### + +The ActionMapper fetches the ActionMapping object corresponding to a given request\. Essentially, the ActionMapping is a data transfer object that collects together details such as the Action class and method to execute\. The mapping is utilized by the Dispatcher and various user interface components\. It is customizable through + +~~~~~~~ +struts.mapper.class +~~~~~~~ + entry in + +~~~~~~~ +struts.properties +~~~~~~~ + or + +~~~~~~~ +struts.xml +~~~~~~~ +\. Note that the value of this constant is the name of the bean of the new mapper\. + +#####Customize##### + +Custom ActionMapper must implement ActionMapper interface and have a default constructor\. + +> + +~~~~~~~ +<bean type="org.apache.struts2.dispatcher.mapper.ActionMapper" name="mymapper" class="com.mycompany.myapp.MyActionMapper" /> +<constant name="struts.mapper.class" value="mymapper" /> + +~~~~~~~ + + +~~~~~~~ +public class MyCustomActionMapper implements ActionMapper { + public ActionMapping getMapping(HttpServletRequest request, + ConfigurationManager configManager) { + .... + } + + public String getUriFromActionMapping(ActionMapping mapping) { + .... + } +} + +~~~~~~~ + +(light\-on) See also: [RestfulActionMapper](restful-action-mapper.html) http://git-wip-us.apache.org/repos/asf/struts-site/blob/f903a0f7/source/core-developers/action-proxy-and-actionproxy-factory.md ---------------------------------------------------------------------- diff --git a/source/core-developers/action-proxy-and-actionproxy-factory.md b/source/core-developers/action-proxy-and-actionproxy-factory.md new file mode 100644 index 0000000..f302bd1 --- /dev/null +++ b/source/core-developers/action-proxy-and-actionproxy-factory.md @@ -0,0 +1,81 @@ +--- +layout: core-developers +title: Action Proxy & ActionProxy Factory +--- + +# Action Proxy & ActionProxy Factory + +The ActionProxy obtains the Action class and calls the appropriate method\. By default, Actions are obtained through local instantiation, but an Action could also be obtained remotely if an alternative ActionProxy were provided\. + +An alternative ActionProxy can be configured through ActionProxyFactory\. Typically, an ActionProxy will utilize the ActionInvocation to encapsulate the execution of a particular request\. + +The ActionInvocation determines how an Action is handled: Is it being intercepted? Is there a PreResultListener acting on it? + +Essentially, ActionProxy encapsulates how an Action can be obtained\. ActionInvocation encapsulates how the Action is executed when a request is invoked\. + +####Customization#### + +#####ActionProxyFactory##### + + + +~~~~~~~ + + ActionProxyFactory.setFactory(new MyActionProxyFactory() { + + // Do something interesting... + + }); + +~~~~~~~ + +#####ActionProxy##### + + + +~~~~~~~ + + ActionProxyFactory.getFactory(new MyActionProxyFactory() { + .... + public ActionProxy createActionProxy(Configuration config, + String namespace, String actionName, Map extraContext) + throws Exception { + createActionProxy(config, namespace, actionName, extraContext, true); + } + public ActionProxy createActionProxy(Configuration config, + String namespace, String actionName, Map extraContext, + boolean executeResult, boolean cleanupContext) + throws Exception { + .... + } + .... + }); + +~~~~~~~ + +#####ActionInvocation##### + + + +~~~~~~~ + + ActionProxyFactory.getFactory(new MyActionProxyFactory() { + ... + public ActionInvocation createActionInvocation(ActionProxy actionProxy) + throws Exception { + createActionInvocation(actionProxy, new LinkedHashMap()); + } + public ActionInvocation createActionInvocation(ActionProxy actionProxy, + Map extraContext) throws Exception { + createActionInvocation(actionProxy, extraContext, true); + } + public ActionInvocation createActionInvocation(ActionProxy actionProxy, + Map extraContext, boolean pushAction) + throws Exception { + // Do implementation of ActionInvocation here + ..... + } + ... + }); + +~~~~~~~ http://git-wip-us.apache.org/repos/asf/struts-site/blob/f903a0f7/source/core-developers/after-annotation.md ---------------------------------------------------------------------- diff --git a/source/core-developers/after-annotation.md b/source/core-developers/after-annotation.md new file mode 100644 index 0000000..f742de9 --- /dev/null +++ b/source/core-developers/after-annotation.md @@ -0,0 +1,38 @@ +--- +layout: core-developers +title: After Annotation +--- + +# After Annotation + +####After Annotation#### + + + +~~~~~~~ +{snippet:id=description|javadoc=true|url=com.opensymphony.xwork2.interceptor.annotations.After} +~~~~~~~ + +#####Usage##### + + + +~~~~~~~ +{snippet:id=usage|javadoc=true|url=com.opensymphony.xwork2.interceptor.annotations.After} +~~~~~~~ + +#####Parameters##### + + + +~~~~~~~ +{snippet:id=parameters|javadoc=true|url=com.opensymphony.xwork2.interceptor.annotations.After} +~~~~~~~ + +#####Examples##### + + + +~~~~~~~ +{snippet:id=example|javadoc=true|lang=java|url=com.opensymphony.xwork2.interceptor.annotations.After} +~~~~~~~ \ No newline at end of file http://git-wip-us.apache.org/repos/asf/struts-site/blob/f903a0f7/source/core-developers/ajax-client-side-validation.md ---------------------------------------------------------------------- diff --git a/source/core-developers/ajax-client-side-validation.md b/source/core-developers/ajax-client-side-validation.md new file mode 100644 index 0000000..31ba46f --- /dev/null +++ b/source/core-developers/ajax-client-side-validation.md @@ -0,0 +1,565 @@ +--- +layout: core-developers +title: AJAX Client Side Validation +--- + +# AJAX Client Side Validation + + +This validation mode only works with the _ajax theme_ + +| + +AJAX\-based client side validation improves upon [Pure JavaScript Client Side Validation](#PAGE_14262) by using a combination of JavaScript, DOM manipulation, and remote server communication\. Unlike the pure client side implementation, AJAX\-based validation communicates with the server\. This means all your validation rules that worked when submitting a form will still work within the browser\. + +The validation occurs on each **onblur** event for each form element\. As each user types in some values and moves to the next form element, the value (and all other values previously entered) will be sent to the server for validation\. The entire validation stack is run, including visitor validators and your action's validate() method\. + +If there is an error, like the pure implementation, the HTML and DOM will be updated immediately\. + +For an example of this, see _AJAX Validation_ \. + +__AJAX Validation__ + + +__Description__ + +Struts provides [client side validation](client-side-validation.html)(using JavaScript) for a few validators\. Using AJAX validation, all [validators](#PAGE_14292) available to the application on the server side can be used without forcing the page to reload, just to show validation errors\. AJAX validation has a server side, which is in included in _JSON Plugin_ (an interceptor and a result)\. Client side must be handled by applictions themself\. One reason for that is there are too many JavaScript frameworks and libraries\. Struts has no preference which of them you use\. Previous versions of Struts included a client side which was relying on the Dojo JS framework and was located in Struts Dojo plugin\. That has been deprecated for a long time and was eventually removed\. + +__Example__ + +This example is taken from the Struts showcase application\. + +__Create the action class__ + + + +~~~~~~~ +public class AjaxFormSubmitAction extends ActionSupport { + private String requiredValidatorField = null; + private String requiredStringValidatorField = null; + private Integer integerValidatorField = null; + private Date dateValidatorField = null; + private String emailValidatorField = null; + private String urlValidatorField = null; + private String stringLengthValidatorField = null; + private String regexValidatorField = null; + private String fieldExpressionValidatorField = null; + @Override + public void validate() { + if (hasFieldErrors()) { + addActionError("Errors present!"); + } + } + public Date getDateValidatorField() { + return dateValidatorField; + } + @DateRangeFieldValidator( + min="01/01/1990", + max="01/01/2000", + message="must be a min 01-01-1990 max 01-01-2000 if supplied") + public void setDateValidatorField(Date dateValidatorField) { + this.dateValidatorField = dateValidatorField; + } + public String getEmailValidatorField() { + return emailValidatorField; + } + @EmailValidator(message="must be a valid email if supplied") + public void setEmailValidatorField(String emailValidatorField) { + this.emailValidatorField = emailValidatorField; + } + public Integer getIntegerValidatorField() { + return integerValidatorField; + } + @IntRangeFieldValidator(min="1", max="10", message="must be integer min 1 max 10 if supplied") + public void setIntegerValidatorField(Integer integerValidatorField) { + this.integerValidatorField = integerValidatorField; + } + public String getRegexValidatorField() { + return regexValidatorField; + } + @RegexFieldValidator( + regex="[^<>]+", + message="regexValidatorField must match a regexp (.*\\.txt) if specified") + public void setRegexValidatorField(String regexValidatorField) { + this.regexValidatorField = regexValidatorField; + } + public String getRequiredStringValidatorField() { + return requiredStringValidatorField; + } + @RequiredStringValidator(trim=true, message="required and must be string") + public void setRequiredStringValidatorField(String requiredStringValidatorField) { + this.requiredStringValidatorField = requiredStringValidatorField; + } + public String getRequiredValidatorField() { + return requiredValidatorField; + } + @RequiredFieldValidator(message="required") + public void setRequiredValidatorField(String requiredValidatorField) { + this.requiredValidatorField = requiredValidatorField; + } + public String getStringLengthValidatorField() { + return stringLengthValidatorField; + } + @StringLengthFieldValidator( + minLength="2", + maxLength="4", + trim=true, + message="must be a String of a specific greater than 1 less than 5 if specified") + public void setStringLengthValidatorField(String stringLengthValidatorField) { + this.stringLengthValidatorField = stringLengthValidatorField; + } + public String getFieldExpressionValidatorField() { + return fieldExpressionValidatorField; + } + @FieldExpressionValidator( + expression = "(fieldExpressionValidatorField == requiredValidatorField)", + message = "must be the same as the Required Validator Field if specified") + public void setFieldExpressionValidatorField( + String fieldExpressionValidatorField) { + this.fieldExpressionValidatorField = fieldExpressionValidatorField; + } + public String getUrlValidatorField() { + return urlValidatorField; + } + @UrlValidator(message="must be a valid url if supplied") + public void setUrlValidatorField(String urlValidatorField) { + this.urlValidatorField = urlValidatorField; + } +} +~~~~~~~ + + + +__Map the Action__ + +Note that is is not necessary when using _Convention Plugin_ \. + + +~~~~~~~ +<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> + +<struts> + <package> + <action name="ajaxFormSubmit" class="org.apache.struts2.showcase.validation.AjaxFormSubmitAction"> + <interceptor-ref name="jsonValidationWorkflowStack"/> + <result name="input">/WEB-INF/validation/ajaxFormSubmit.jsp</result> + <result type="jsonActionRedirect">ajaxFormSubmitSuccess</result> + </action> + </package> + +~~~~~~~ + +AJAX validation is performed by the _jsonValidation_ interceptor\. This interceptor is included in the _jsonValidationWorkflowStack_ , and is required in order to perform AJAX validation\. Normal results(input, success, etc) should be provided for the action in the case that someone tries to access the action directly, in which case normal validation will be triggered\. So, how does the _jsonValidation_ know that it must perform AJAX validation vs regular validation? We will see that in a minute, but you don't need to know that in order to use AJAX validation\. Same applies for specialized Redirect Result Type _jsonActionRedirect_ \. + +__Create the JSP__ + + + +~~~~~~~ +<%@taglib prefix="s" uri="/struts-tags" %> +<html> +<head> + <title>Struts2 Showcase - Validation - AJAX Form Submit</title> + <s:head theme="xhtml"/> +</head> +<body> +<div class="page-header"> + <h1>AJAX Form Submit</h1> +</div> + <h3>Action Errors Will Appear Here</h3> + <s:actionerror theme="ajaxErrorContainers"/> + <hr/> + <s:form method="POST" theme="xhtml"> + <s:textfield label="Required Validator Field" name="requiredValidatorField" theme="ajaxErrorContainers"/> + <s:textfield label="Required String Validator Field" name="requiredStringValidatorField" theme="ajaxErrorContainers"/> + <s:textfield label="Integer Validator Field" name="integerValidatorField" theme="ajaxErrorContainers"/> + <s:textfield label="Date Validator Field" name="dateValidatorField" theme="ajaxErrorContainers"/> + <s:textfield label="Email Validator Field" name="emailValidatorField" theme="ajaxErrorContainers"/> + <s:textfield label="URL Validator Field" name="urlValidatorField" theme="ajaxErrorContainers"/> + <s:textfield label="String Length Validator Field" name="stringLengthValidatorField" theme="ajaxErrorContainers"/> + <s:textfield label="Regex Validator Field" name="regexValidatorField" theme="ajaxErrorContainers"/> + <s:textfield label="Field Expression Validator Field" name="fieldExpressionValidatorField" theme="ajaxErrorContainers"/> + <s:submit label="Submit" cssClass="btn btn-primary"/> + </s:form> +</body> +</html> +~~~~~~~ + +Things to note on this JSP: + ++ The _form_ tag **does not** have _validate_ set to _true_ , which would perform client validation before the AJAX validation\. + ++ It uses a customized theme _ajaxErrorContainers_ \. The default Struts themes generate HTML\-Elements to show validation errors only if errors are present when page is created on server side\. But in order to show validation errors that arrive later via AJAX it is necessary to have error\-container elements in DOM always\. + +What happens if validation succeeds? That depends on your request parameters and action configuration\. If you are using _jsonActionRedirect_ result mentioned above the action will be executed while AJAX request is active and respond with JSON providing a new URL to load\. Otherwise the AJAX response will be empty and the form must be submitted a 2nd time but as usual request, not AJAX\. + + + +| Setting _validate_ to _true_ in the _form_ tag will enable client side, JavaScript validation, which can be used along with AJAX validation (runs before the AJAX validation)\. + +| + +__Custom Theme__ + +In this sample the _custom theme_ is based on _xhtml_ theme\. It is required to override 3 FTL files\. + + + +> theme\.properties + + + +~~~~~~~ +parent = xhtml +~~~~~~~ + + + + + +> actionerror\.ftl + + + +~~~~~~~ +<#-- + Make sure element is always present. To be filled later via JS. +--> +<ul<#rt/> +<#if parameters.id??> + id="${parameters.id?html}"<#rt/> +</#if> +<#if parameters.cssClass??> + class="${parameters.cssClass?html}"<#rt/> +<#else> + class="errorMessage"<#rt/> +</#if> +<#if parameters.cssStyle??> + style="${parameters.cssStyle?html}"<#rt/> +</#if> +> +<#if (actionErrors?? && actionErrors?size > 0)> + <#list actionErrors as error> + <#if error??> + <li><span><#if parameters.escape>${error!?html}<#else>${error!}</#if></span><#rt/></li><#rt/> + </#if> + </#list> +</#if> +</ul> +~~~~~~~ + + + + + +> controlfooter\.ftl + + + +~~~~~~~ +${parameters.after!}<#t/> + </td><#lt/> +</tr> +<#if (parameters.errorposition!"top") == 'bottom'> +<#assign hasFieldErrors = parameters.name?? && fieldErrors?? && fieldErrors[parameters.name]??/> +<#if hasFieldErrors> +<tr errorFor="${parameters.id}"> + <td class="tdErrorMessage" colspan="2"><#rt/> + <#if hasFieldErrors> + <#list fieldErrors[parameters.name] as error> + <div class="errorMessage">${error?html}</div><#t/> + </#list> + </#if> + </td><#lt/> +</tr> +</#if> +</#if> + + +~~~~~~~ + + + + + +> controlheader\-core\.ftl + + + +~~~~~~~ + <#-- + Always include elements to show errors. They may be filled later via AJAX. +--> +<#assign hasFieldErrors = parameters.name?? && fieldErrors?? && fieldErrors[parameters.name]??/> +<#if (parameters.errorposition!"top") == 'top'> +<tr errorFor="${parameters.id}"> + <td class="tdErrorMessage" colspan="2" data-error-for-fieldname="${parameters.name}"><#rt/> + <#if hasFieldErrors> + <#list fieldErrors[parameters.name] as error> + <div class="errorMessage">${error?html}</div><#t/> + </#list> + </#if> + </td><#lt/> +</tr> +</#if> +<#if !parameters.labelposition?? && (parameters.form.labelposition)??> +<#assign labelpos = parameters.form.labelposition/> +<#elseif parameters.labelposition??> +<#assign labelpos = parameters.labelposition/> +</#if> +<#-- + if the label position is top, + then give the label it's own row in the table +--> +<tr> +<#if (labelpos!"") == 'top'> + <td class="tdLabelTop" colspan="2"><#rt/> +<#else> + <td class="tdLabel"><#rt/> +</#if> +<#if parameters.label??> + <label <#t/> +<#if parameters.id??> + for="${parameters.id?html}" <#t/> +</#if> +<#if hasFieldErrors> + class="errorLabel"<#t/> +<#else> + class="label"<#t/> +</#if> + ><#t/> +<#if parameters.required!false && parameters.requiredPosition!"right" != 'right'> + <span class="required">*</span><#t/> +</#if> +${parameters.label?html}<#t/> +<#if parameters.required!false && parameters.requiredPosition!"right" == 'right'> + <span class="required">*</span><#t/> +</#if> +${parameters.labelseparator!":"?html}<#t/> +<#include "/${parameters.templateDir}/${parameters.expandTheme}/tooltip.ftl" /> +</label><#t/> +</#if> + </td><#lt/> +<#-- add the extra row --> +<#if (labelpos!"") == 'top'> +</tr> +<tr> +</#if> + + +~~~~~~~ + +__CSS__ + +To show users some nice visual feedback while waiting for AJAX response you can use a little CSS\. Remember to include the referenced _indicator\.gif_ \. + + +~~~~~~~ +.ajaxVisualFeedback { + width: 16px; + height: 16px; + background-image: url('../images/indicator.gif'); + background-repeat: no-repeat; + float: right; +} +~~~~~~~ + + + +__JavaScript__ + +Now this is where the magic happens\. Here _jQuery_ is used to register an eventhandler which intercepts form submits\. It takes care of hiding validation errors that might be present, submit the form via AJAX and handle JSON responses\. + + +~~~~~~~ + /** + * Validates form per AJAX. To be called as onSubmit handler. + * + * @param event onSubmit event + */ +function ajaxFormValidation(event) { + event.preventDefault(); + _removeValidationErrors(); + var _form = $(event.target); + var _formData = _form.serialize(true); + // prepare visual feedback + // you may want to use other elements here + var originalButton = _form.find('.btn-primary'); + // note: jQuery returns an array-like object + if (originalButton && originalButton.length && originalButton.length > 0) { + originalButton.hide(); + var feedbackElement = $('<div class="ajaxVisualFeedback"></div>').insertAfter(originalButton); + var restoreFunction = function() { + originalButton.show(); + feedbackElement.remove(); + } + } + var options = { + data: 'struts.enableJSONValidation=true&struts.validateOnly=false&' + _formData, + async: true, + processData: false, + type: 'POST', + success: function (response, statusText, xhr) { + if (response.location) { + // no validation errors + // action has been executed and sent a redirect URL wrapped as JSON + // cannot use a normal http-redirect (status-code 3xx) as this would be followed by browsers and would not be available here + // follow JSON-redirect + window.location.href = response.location; + } else { + if (restoreFunction) { + restoreFunction(); + } + _handleValidationResult(_form, response); + } + }, + error: function(xhr, textStatus, errorThrown) { + if (restoreFunction) { + restoreFunction(); + } + // struts sends status code 400 when validation errors are present + if (xhr.status === 400) { + _handleValidationResult(_form, JSON.parse(xhr.responseText)) + } else { + // a real error occurred -> show user an error message + _handleValidationResult(_form, {errors: ['Network or server error!']}) + } + } + } + // send request, after delay to make sure everybody notices the visual feedback :) + window.setTimeout(function() { + var url = _form[0].action; + jQuery.ajax(url, options); + }, 1000); +} +/** + * Removes validation errors from HTML DOM. + */ +function _removeValidationErrors() { + // action errors + // you might want to use a custom ID here + $('ul.errorMessage li').remove(); + // field errors + $('div.errorMessage').remove(); +} +/** + * Incorporates validation errors in HTML DOM. + * + * @param form Form containing errors. + * @param errors Errors from server. + */ +function _handleValidationResult(form, errors) { + // action errors + if (errors.errors) { + // you might want to use a custom ID here + var errorContainer = $('ul.errorMessage'); + $.each(errors.errors, function(index, errorMsg) { + var li = $('<li><span></span></li>'); + li.text(errorMsg); // use text() for security reasons + errorContainer.append(li); + }); + } + // field errors + if (errors.fieldErrors) { + $.each(errors.fieldErrors, function(fieldName, errorMsg) { + var td = $('td[data-error-for-fieldname="' + fieldName + '"]'); + if (td) { + var div = $('<div class="errorMessage"></div>'); + div.text(errorMsg); // use text() for security reasons + td.append(div); + } + }); + } +} +// register onSubmit handler +$(window).bind('load', function() { + $('form').bind('submit', ajaxFormValidation); +}); +~~~~~~~ + + + +__How it works__ + +_jsonValidation_ interceptor must be placed on a stack, following the _validation_ interceptor\. The interceptor itself won't perform any validation, but will check for validation errors on the action being invoked (assuming that the action is ValidationAware)\. + +If you just want to use AJAX validation, without knowing the implementation details, you can skip this section\. + +When the _jsonValidation_ interceptor is invoked, it will look for a parameter named _struts\.enableJSONValidation_ , this parameter **must** be set to _true_ , otherwise the interceptor won't do anything\. Then the interceptor will look for a parameter named _struts\.validateOnly_ , if this parameter exists, is set to _true_ , and there are validation errors (o action errors) they will be serialized into JSON in the form: + + +~~~~~~~ +{ + "errors": ["Global Error 1", "Global Error 2"], + "fieldErrors": { + "field1": ["Field 1 Error 1", "Field 1 Error 2"], + "field1": ["Field 2 Error 1", "Field 2 Error 2"] + } +} + +~~~~~~~ + +If the action implements the _ModelDrive_ interface, "model\." will be stripped from the field names in the returned JSON\. If validation succeeds (and _struts\.validateOnly_ is true), an empty JSON string will be returned: + + +~~~~~~~ +{} + +~~~~~~~ + +If _struts\.validateOnly_ is false the action and result are executed\. In this case _jsonActionRedirect_ result is very useful\. It creates a JSON response in the form: + + +~~~~~~~ +{"location": "<url to be loaded next>"} + +~~~~~~~ + + +> + +> + +> Remember to set struts\.enableJSONValidation=true in the request to enable AJAX validation + +> + +__JSONValidationInterceptor parameters__ + +The following request parameters can be used to enable exposing validation errors: + ++ **struts\.enableJSONValidation** \- a request parameter must be set to **true** to use this interceptor + ++ **struts\.validateOnly** \- If the request has this parameter, execution will return after validation (action won't be executed)\. If **struts\.validateOnly** is set to false you may want to use _JSONActionRedirectResult_ + ++ **struts\.JSONValidation\.no\.encoding** \- If the request has this parameter set to **true,** the character encoding will **NOT** be set on the response \- is needed in portlet environment + +You can override names of these parameters by specifying the following parameters when setting up a stack: + ++ **validateJsonParam** \- to override name of **struts\.enableJSONValidation**** +** + ++ **validateOnlyParam** \- to override name of **struts\.validateOnly** + ++ **noEncodingSetParam** \- to override name of **struts\.JSONValidation\.no\.encoding** + ++ **validationFailedStatus** \- status to be set on response when there are validation errors, by default **400** + +** +** + + Parameters overriding is available since Struts 2\.5\.9 + +__Flow chart of AJAX validation__ + + +Some details are omitted, like results used\. + +| + + +As explained above: there is a case where form is submitted twice, one time as AJAX with validation only and another time as usual submit\. + +| + +_struts2\-ajax\-vali\-flow\.png_ http://git-wip-us.apache.org/repos/asf/struts-site/blob/f903a0f7/source/core-developers/ajax.md ---------------------------------------------------------------------- diff --git a/source/core-developers/ajax.md b/source/core-developers/ajax.md new file mode 100644 index 0000000..b8a39be --- /dev/null +++ b/source/core-developers/ajax.md @@ -0,0 +1,144 @@ +--- +layout: core-developers +title: AJAX +--- + +# AJAX + +AJAX is an acronym for Asynchronous JavaScript and XML\. Essentially, a JavaScript can make a HTTP request and update portions of a page directly, without going through a conventional POST or GET and refreshing the entire page\. Better yet, a page can contain several JavaScripts making simultaneous (asynchronous) requests\. + +The key point is that when a script makes an "Ajax request" (XHR), the server doesn't know it came from a script, and handles it like any other request\. One reason Ajax is so successful is that it works just fine with existing server technologies, including Struts\. + +It's not the Ajax request that is different, but the Ajax response\. Instead of returning an entire page for the browser to display (or redisplay), an Ajax response will just return a portion of a page\. The response can take the form of XML, or HTML, or plain text, another script, or whatever else the calling script may want\. + +Both Struts 1 and Struts 2 can return any type of response\. We are not limited to forwarding to a server page\. In Struts 1, you can just do something like: + + +~~~~~~~ +response.setContentType("text/html"); +PrintWriter out = response.getWriter(); +out.println("Hello World! This is an AJAX response from a Struts Action."); +out.flush(); +return null; + +~~~~~~~ + +In Struts 2, we can do the same thing with a Stream result\. + + + +| Using a Struts 2 plugin (e\.g\., _JSON plugin_ , jQuery plugin, etc\.) is, in general, preferred to writing the response directly from within an action\. See sections following this for further details\. + +| + +**Struts 2 Stream result Action** + + +~~~~~~~ +package actions; + +import java.io.InputStream; +import java.io.StringBufferInputStream; +import com.opensymphony.xwork2.ActionSupport; + +public class TextResult extends ActionSupport { + private InputStream inputStream; + public InputStream getInputStream() { + return inputStream; + } + + public String execute() throws Exception { + inputStream = new ByteArrayInputStream("Hello World! This is a text string response from a Struts 2 Action.".getBytes("UTF-8")); + return SUCCESS; + } +} + +~~~~~~~ + +**Struts 2 Configuring the TextResult Action** + + +~~~~~~~ +<action name="text-result" class="actions.TextResult"> + <result type="stream"> + <param name="contentType">text/html</param> + <param name="inputName">inputStream</param> + </result> +</action> + +~~~~~~~ + + (ok) Struts 2 can also return a JSON (JavaScript Object Notation) response, using a [plugin](http://cwiki\.apache\.org/S2PLUGINS/json\-plugin\.html)^[http://cwiki\.apache\.org/S2PLUGINS/json\-plugin\.html]\. + +On the client side, there are two basic strategies, which can be mixed and matched\. + +First, you can use some type of JSP tag\. Here, you don't have to know very much at all about Ajax or JavaScript\. The taglib does all the work, and you just have to figure out how to use the taglib\. The standard Struts 2 taglib includes several _Ajax JSP tags_ , and many third\-party libraries are available, including: + ++ [Ajax Tags](http://ajaxtags\.sourceforge\.net/)^[http://ajaxtags\.sourceforge\.net/] + ++ [AjaxParts Taglib](http://javawebparts\.sourceforge\.net/)^[http://javawebparts\.sourceforge\.net/] + ++ [ColdTags Suite](http://servletsuite\.blogspot\.com/2006/06/coldtags\-suite\-ajax\-edition\.html)^[http://servletsuite\.blogspot\.com/2006/06/coldtags\-suite\-ajax\-edition\.html] + ++ [Prize Tags](http://www\.jenkov\.com/prizetags/introduction\.tmpl)^[http://www\.jenkov\.com/prizetags/introduction\.tmpl] + ++ [JSON\-taglib](http://json\-taglib\.sourceforge\.net/)^[http://json\-taglib\.sourceforge\.net/] + +Alternatively, you can use a plain\-old Ajax widget on a plain\-old HTML page, using libraries like [Dojo](http://dojotoolkit\.org/)^[http://dojotoolkit\.org/], [JQuery](http://jquery\.com/)^[http://jquery\.com/], or [YUI](http://developer\.yahoo\.com/yui/)^[http://developer\.yahoo\.com/yui/], and the StreamResult or the [JSON Plugin](http://cwiki\.apache\.org/S2PLUGINS/json\-plugin\.html)^[http://cwiki\.apache\.org/S2PLUGINS/json\-plugin\.html]\. Here, the sky's the limit, but you actually have to learn something about JavaScript as a language\. + +####Ajax Plugins#### + +While Struts works fine with Ajax out\-of\-the\-box, for added value, several Ajax\-centric plugins are available\. + +#####Ajax Tag Plugins##### + ++ **jQuery** \- The [jQuery Plugin](https://github\.com/struts\-community\-plugins/struts2\-jquery)^[https://github\.com/struts\-community\-plugins/struts2\-jquery] provide ajax functionality and UI Widgets an JavaScript Grid based on the jQuery javascript framework\.** +** + ++ **Ajax Parts** \- The [AjaxParts Taglib (APT)](http://code\.google\.com/p/struts2ajaxpartstaglibplugin/)^[http://code\.google\.com/p/struts2ajaxpartstaglibplugin/] is a component of the Java Web Parts (JWP) project ([http://javawebparts\.sourceforge\.net](http://javawebparts\.sourceforge\.net)) that allows for 100% declarative (read: no Javascript coding required\!) AJAX functionality within a Java\-based webapp\. + ++ **Dojo** \- The _Ajax Tags Dojo Plugin_ was represented as a theme for Struts 2\.0\. For Struts 2\.1, the Dojo tags are bundled as a plugin until version 2\.3\.x\. Since version 2\.5 this plugin is not part of th Struts2 distribution anymore + ++ **YUI** \- The [Yahoo User Interface (YUI) Plugin](https://code\.google\.com/p/struts2yuiplugin/)^[https://code\.google\.com/p/struts2yuiplugin/] has only a few tags are available so far, but the YUI tags tend to be easier to use than the Dojo versions\. + +#####Other Ajax Plugins##### + ++ **Ajax File Upload** \- With the [Ajax File Upload Plugin](http://www\.davidjc\.com/ajaxfileupload/demo\!input\.action)^[http://www\.davidjc\.com/ajaxfileupload/demo\!input\.action] we can upload a file to the server and asynchronously monitor its progress\. + ++ **GWT** \- The [Google Web Toolkit Plugin](https://code\.google\.com/p/struts2gwtplugin/)^[https://code\.google\.com/p/struts2gwtplugin/] exposes Struts 2 actions to the GWT RPC mechanism\. + ++ **JSON** \- The _JSON Plugin_ serializes Actions properties into JSON, making it easy to respond to JavaScript requests\. + +See the [Struts Plugin Repository](http://cwiki\.apache\.org/S2PLUGINS/home\.html)^[http://cwiki\.apache\.org/S2PLUGINS/home\.html] for a complete list of Struts 2 plugins\. + +####Ajax Results with JSP#### + +While server pages are most often used to generate HTML, we can use server pages to create other types of data streams\. Here's an example: + +**book\.jsp** + + +~~~~~~~ +<%@ page import="java.util.Iterator, + java.util.List, + com.esolaria.dojoex.Book, + com.esolaria.dojoex.BookManager" %> +<% + String bookIdStr = request.getParameter("bookId"); + int bookId = (bookIdStr == null || "".equals(bookIdStr.trim())) + ? 0 : Integer.parseInt(bookIdStr); + Book book = BookManager.getBook(bookId); + if (book != null) { + out.println(book.toJSONString()); + System.out.println("itis: " + book.toJSONString()); + } +%> + +~~~~~~~ + +In the code example, we use + +~~~~~~~ +System.out.println +~~~~~~~ + to return a JSON data stream as the response\. For more about this technique, see the article [Using Dojo and JSON to Build Ajax Applications](http://today\.java\.net/pub/a/today/2006/04/27/building\-ajax\-with\-dojo\-and\-json\.html)^[http://today\.java\.net/pub/a/today/2006/04/27/building\-ajax\-with\-dojo\-and\-json\.html]\. http://git-wip-us.apache.org/repos/asf/struts-site/blob/f903a0f7/source/core-developers/alias-interceptor.md ---------------------------------------------------------------------- diff --git a/source/core-developers/alias-interceptor.md b/source/core-developers/alias-interceptor.md new file mode 100644 index 0000000..35aeedd --- /dev/null +++ b/source/core-developers/alias-interceptor.md @@ -0,0 +1,36 @@ +--- +layout: core-developers +title: Alias Interceptor +--- + +# Alias Interceptor + + + +~~~~~~~ +{snippet:id=description|javadoc=true|url=com.opensymphony.xwork2.interceptor.AliasInterceptor} +~~~~~~~ + +#####Parameters##### + + + +~~~~~~~ +{snippet:id=parameters|javadoc=true|url=com.opensymphony.xwork2.interceptor.AliasInterceptor} +~~~~~~~ + +#####Extending the Interceptor##### + + + +~~~~~~~ +{snippet:id=extending|javadoc=true|url=com.opensymphony.xwork2.interceptor.AliasInterceptor} +~~~~~~~ + +#####Examples##### + + + +~~~~~~~ +{snippet:id=example|lang=xml|javadoc=true|url=com.opensymphony.xwork2.interceptor.AliasInterceptor} +~~~~~~~ http://git-wip-us.apache.org/repos/asf/struts-site/blob/f903a0f7/source/core-developers/annotation-workflow-interceptor.md ---------------------------------------------------------------------- diff --git a/source/core-developers/annotation-workflow-interceptor.md b/source/core-developers/annotation-workflow-interceptor.md new file mode 100644 index 0000000..1d575b4 --- /dev/null +++ b/source/core-developers/annotation-workflow-interceptor.md @@ -0,0 +1,49 @@ +--- +layout: core-developers +title: AnnotationWorkflowInterceptor +--- + +# AnnotationWorkflowInterceptor + +~~~~~~~ +{snippet:id=javadoc|javadoc=true|url=com.opensymphony.xwork2.interceptor.annotations.AnnotationWorkflowInterceptor} +~~~~~~~ + +#####Examples##### + + + +~~~~~~~ +{snippet:id=javacode|javadoc=true|lang=java|url=com.opensymphony.xwork2.interceptor.annotations.AnnotationWorkflowInterceptor} +~~~~~~~ + +Configure a stack in struts\.xml that replaces the PrepareInterceptor with the AnnotationWorkflowInterceptor: + + +~~~~~~~ + +<interceptor-stack name="annotatedStack"> + <interceptor-ref name="static-params"/> + <interceptor-ref name="params"/> + <interceptor-ref name="conversionError"/> + <interceptor-ref name="annotationWorkflow"/> +</interceptor-stack> + +~~~~~~~ + +Given an Action, AnnotatedAction, add a reference to the AnnotationWorkflowInterceptor interceptor\. + + +~~~~~~~ + +<action name="AnnotatedAction" class="com.examples.AnnotatedAction"> + <interceptor-ref name="annotationWorkflow"/> + <result name="success" type="freemarker">good_result.ftl</result> +</action> + +~~~~~~~ + + +~~~~~~~ +{snippet:id=example|javadoc=true|url=com.opensymphony.xwork2.interceptor.annotations.AnnotationWorkflowInterceptor} +~~~~~~~
