[S2] ExecuteAndWait Interceptor // Only re-submit token parameters on refresh?

classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|

[S2] ExecuteAndWait Interceptor // Only re-submit token parameters on refresh?

Burton Rhodes
I have been getting "Bad Request" or "URL too long" errors on occasion for
an email form that uses the execute and wait interceptor.  I am using the
<meta http> to resubmit the form per the documentation.

<meta http-equiv="refresh" content="3;url=<s:url includeParams="all"/>"/>

However, the original form submits via POST and the meta tag uses GET which
I believe is the source of the issue.  I've tried the code below thinking
that I could omit all the parameters except the token, but this doesn't
seem to do the trick.  Struts wants the entire form (again) - without all
the parameters the validate methods fail for the action class.

<meta http-equiv="refresh" content="3;url=<s:url>
    <s:param name="struts.token.name" value="%{tokenName}" />
    <s:param name="token" value="%{token}" />
</s:url>"/>

Any ideas on how to implement execute and wait correctly without submitting
the entire form via GET parameters each time a refresh that happens?

Thanks!
Burton
Reply | Threaded
Open this post in threaded view
|

Re: [S2] ExecuteAndWait Interceptor // Only re-submit token parameters on refresh?

Yasser Zamani-2


On 4/15/2018 11:39 PM, Burton Rhodes wrote:

> I have been getting "Bad Request" or "URL too long" errors on occasion for
> an email form that uses the execute and wait interceptor.  I am using the
> <meta http> to resubmit the form per the documentation.
>
> <meta http-equiv="refresh" content="3;url=<s:url includeParams="all"/>"/>
>
> However, the original form submits via POST and the meta tag uses GET which
> I believe is the source of the issue.  I've tried the code below thinking
> that I could omit all the parameters except the token, but this doesn't
> seem to do the trick.  Struts wants the entire form (again) - without all
> the parameters the validate methods fail for the action class.
>
> <meta http-equiv="refresh" content="3;url=<s:url>
>     <s:param name="struts.token.name" value="%{tokenName}" />
>     <s:param name="token" value="%{token}" />
> </s:url>"/>
>
> Any ideas on how to implement execute and wait correctly without submitting
> the entire form via GET parameters each time a refresh that happens?

Could you try including all except the email body field? I guess this
issue happens because the email body usually is too long to being
included inside url query string. To confirm, could you increase refresh
timeout to have time to right-click on your browser page then click view
source? I'm interested what is generated by <meta http-equiv="refresh"
content="3;url=<s:url includeParams="all"/>"/> there when there is a
long email body posted!

Thanks in advance!

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]
Reply | Threaded
Open this post in threaded view
|

Re: [S2] ExecuteAndWait Interceptor // Only re-submit token parameters on refresh?

Burton Rhodes
Yes. I believe this is the case. Let me see if I can track down an example
that breaks. Right now I just have reports of this happening but I haven’t
been able to reproduce on my end yet.

Also, How would I include everything except the email body field with the
s:url tag?

Thanks,
Burton


On Monday, April 16, 2018, Yasser Zamani <[hidden email]> wrote:

>
>
> On 4/15/2018 11:39 PM, Burton Rhodes wrote:
> > I have been getting "Bad Request" or "URL too long" errors on occasion
> for
> > an email form that uses the execute and wait interceptor.  I am using the
> > <meta http> to resubmit the form per the documentation.
> >
> > <meta http-equiv="refresh" content="3;url=<s:url includeParams="all"/>"/>
> >
> > However, the original form submits via POST and the meta tag uses GET
> which
> > I believe is the source of the issue.  I've tried the code below thinking
> > that I could omit all the parameters except the token, but this doesn't
> > seem to do the trick.  Struts wants the entire form (again) - without all
> > the parameters the validate methods fail for the action class.
> >
> > <meta http-equiv="refresh" content="3;url=<s:url>
> >     <s:param name="struts.token.name" value="%{tokenName}" />
> >     <s:param name="token" value="%{token}" />
> > </s:url>"/>
> >
> > Any ideas on how to implement execute and wait correctly without
> submitting
> > the entire form via GET parameters each time a refresh that happens?
>
> Could you try including all except the email body field? I guess this
> issue happens because the email body usually is too long to being
> included inside url query string. To confirm, could you increase refresh
> timeout to have time to right-click on your browser page then click view
> source? I'm interested what is generated by <meta http-equiv="refresh"
> content="3;url=<s:url includeParams="all"/>"/> there when there is a
> long email body posted!
>
> Thanks in advance!
>
Reply | Threaded
Open this post in threaded view
|

Re: [S2] ExecuteAndWait Interceptor // Only re-submit token parameters on refresh?

Yasser Zamani-2


On 4/17/2018 6:42 AM, Burton Rhodes wrote:
> Also, How would I include everything except the email body field with the
> s:url tag?

Unfortunately, I couldn't find a simple way :(

However, as a workaround, instead, in your email form jsp, could you put
the email body field as a POST param and the rest short fields which
need validation as GET params (i.e. in a query string inside action url
using s:url)? Because then you can use <meta http-equiv="refresh"
content="3;url=<s:url includeParams="get"/>"/> which only counts GET
params i.e. only query string which doesn't include email body but
includes other validation needed fields.

But if I were you, I prefer to use two actions for such requirement.
action1 which doesn't have ExecAndWait interceptor but has others like
validation, gets form data and stores them as a single object inside
session with a unique key every-time i.e. a GUID. Then action1 redirects
to action2 and also passes that key to action2. action2 behaves like a
mail sender and only has ExecAndWait interceptor. action2 gets then
deletes data from session by that key and the rest is same as your
current single action :)

Regards.

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]
Reply | Threaded
Open this post in threaded view
|

Re: [S2] ExecuteAndWait Interceptor // Only re-submit token parameters on refresh?

Burton Rhodes
That's a great thought.  I think the session method makes more sense.

On Tue, Apr 17, 2018 at 2:12 AM, Yasser Zamani <[hidden email]>
wrote:

>
>
> On 4/17/2018 6:42 AM, Burton Rhodes wrote:
> > Also, How would I include everything except the email body field with the
> > s:url tag?
>
> Unfortunately, I couldn't find a simple way :(
>
> However, as a workaround, instead, in your email form jsp, could you put
> the email body field as a POST param and the rest short fields which
> need validation as GET params (i.e. in a query string inside action url
> using s:url)? Because then you can use <meta http-equiv="refresh"
> content="3;url=<s:url includeParams="get"/>"/> which only counts GET
> params i.e. only query string which doesn't include email body but
> includes other validation needed fields.
>
> But if I were you, I prefer to use two actions for such requirement.
> action1 which doesn't have ExecAndWait interceptor but has others like
> validation, gets form data and stores them as a single object inside
> session with a unique key every-time i.e. a GUID. Then action1 redirects
> to action2 and also passes that key to action2. action2 behaves like a
> mail sender and only has ExecAndWait interceptor. action2 gets then
> deletes data from session by that key and the rest is same as your
> current single action :)
>
> Regards.
>
Reply | Threaded
Open this post in threaded view
|

Re: [S2] ExecuteAndWait Interceptor // Only re-submit token parameters on refresh?

Yasser Zamani-2


On 4/17/2018 3:53 PM, Burton Rhodes wrote:
> That's a great thought.  I think the session method makes more sense.

Glad to hear :) thanks! I would like to add that you also can delete
ExecAndWait interceptor. Then just save posted data in db and
immediately send a thank you message to your user. Then check for and
send emails (e.g. each 10 minutes) e.g. using quartz jar.

Regards.

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]
Reply | Threaded
Open this post in threaded view
|

Re: [S2] ExecuteAndWait Interceptor // Only re-submit token parameters on refresh?

Martin Gainty
In reply to this post by Burton Rhodes


________________________________
From: Burton Rhodes <[hidden email]>
Sent: Tuesday, April 17, 2018 7:23 AM
To: Struts Users Mailing List
Subject: Re: [S2] ExecuteAndWait Interceptor // Only re-submit token parameters on refresh?

That's a great thought.  I think the session method makes more sense.

On Tue, Apr 17, 2018 at 2:12 AM, Yasser Zamani <[hidden email]>
wrote:

>
>
> On 4/17/2018 6:42 AM, Burton Rhodes wrote:
> > Also, How would I include everything except the email body field with the
> > s:url tag?
>
> Unfortunately, I couldn't find a simple way :(
>
> However, as a workaround, instead, in your email form jsp, could you put
> the email body field as a POST param and the rest short fields which
> need validation as GET params (i.e. in a query string inside action url
> using s:url)? Because then you can use <meta http-equiv="refresh"
> content="3;url=<s:url includeParams="get"/>"/> which only counts GET
> params i.e. only query string which doesn't include email body but
> includes other validation needed fields.
>
> But if I were you, I prefer to use two actions for such requirement.
> action1 which doesn't have ExecAndWait interceptor but has others like
> validation, gets form data and stores them as a single object inside
> session with a unique key every-time i.e. a GUID. Then action1 redirects
> to action2 and also passes that key to action2. action2 behaves like a
> mail sender and only has ExecAndWait interceptor. action2 gets then
> deletes data from session by that key and the rest is same as your
> current single action :)

MG>AFAIK a redirect terminates the old session and creates a new session
MG>a better alternative is to implement ChainingInterceptor with <result type="chain"> e.g.
MG>
<action name="someAction" class="com.examples.SomeAction">
     <interceptor-ref name="basicStack"/>
     <result name="success" type="chain">otherAction</result>
 </action>

 <action name="otherAction" class="com.examples.OtherAction">
     <interceptor-ref name="chain"/>
     <interceptor-ref name="basicStack"/>
     <result name="success">good_result.ftl</result>
 </action>
MG>
http://viralpatel.net/blogs/struts-2-action-chaining-example/
Struts 2 Action Chaining example - ViralPatel.net<http://viralpatel.net/blogs/struts-2-action-chaining-example/>
viralpatel.net
In Struts 2, sometimes you may want to process another action when one action completes. For example on successfully submitting a form you want to render output from other action.

MG>?

>
> Regards.
>
Reply | Threaded
Open this post in threaded view
|

Re: [S2] ExecuteAndWait Interceptor // Only re-submit token parameters on refresh?

Yasser Zamani-2


On 4/18/2018 1:21 AM, Martin Gainty wrote:
> MG>AFAIK a redirect terminates the old session and creates a new session

I think redirect to same domain:ip in same browser tab page should keep
session.

> MG>a better alternative is to implement ChainingInterceptor with <result type="chain"> e.g.

As Struts uses action name for the key of the background process saved
into session, I think chain doesn't work for this requirement.

Regards.

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: [S2] ExecuteAndWait Interceptor // Only re-submit token parameters on refresh?

Martin Gainty
MG2>some confusion on where session is accessed

________________________________
From: Yasser Zamani <[hidden email]> on behalf of Yasser Zamani <[hidden email]>
Sent: Wednesday, April 18, 2018 2:57 AM
To: [hidden email]
Subject: Re: [S2] ExecuteAndWait Interceptor // Only re-submit token parameters on refresh?



On 4/18/2018 1:21 AM, Martin Gainty wrote:
> MG>AFAIK a redirect terminates the old session and creates a new session

I think redirect to same domain:ip in same browser tab page should keep
session.

MG2>2 alternatives: build out HTTPGet with &params

 * Calls the {@link HttpServletResponse#sendRedirect(String) sendRedirect}
 * method to the location specified. The response is told to redirect the
 * browser to the specified location (a new request from the client). The
 * consequence of doing this means that the action (action instance, action
 * errors, field errors, etc) that was just executed is lost and no longer
 * available. This is because actions are built on a single-thread model. The
 * only way to pass data is through the session
MG2>with chain interceptor

 * or with web parameters

 * (url?name=value) which can be OGNL expressions.
MG2>essentially a HTTP Get with params............. here is example:
* <!-- START SNIPPET: example -->
 * &lt;!--
 *   The redirect URL generated will be:
 *   /foo.jsp#FRAGMENT
 * --&gt;
 * &lt;result name="success" type="redirect"&gt;
 *   &lt;param name="location"&gt;foo.jsp&lt;/param&gt;
 *   &lt;param name="parse"&gt;false&lt;/param&gt;
 *   &lt;param name="anchor"&gt;FRAGMENT&lt;/param&gt;
 * &lt;/result&gt;
 * <!-- END SNIPPET: example -->
MG2>

MG2>Here is code that *should access session params* to pass to new finalLocation (but session is not accessed?)
    /**
     * Redirects to the location specified by calling
     * {@link HttpServletResponse#sendRedirect(String)}.
     *
     * @param finalLocation the location to redirect to.
     * @param invocation an encapsulation of the action execution state.
     * @throws Exception if an error occurs when redirecting.
     */
    protected void doExecute(String finalLocation, ActionInvocation invocation) throws Exception {
        ActionContext ctx = invocation.getInvocationContext();
        HttpServletRequest request = (HttpServletRequest) ctx.get(ServletActionContext.HTTP_REQUEST);
        HttpServletResponse response = (HttpServletResponse) ctx.get(ServletActionContext.HTTP_RESPONSE);

        if (isPathUrl(finalLocation)) {
            if (!finalLocation.startsWith("/")) {
                ActionMapping mapping = actionMapper.getMapping(request, Dispatcher.getInstance().getConfigurationManager());
                String namespace = null;
                if (mapping != null) {
                    namespace = mapping.getNamespace();
                }

                if ((namespace != null) && (namespace.length() > 0) && (!"/".equals(namespace))) {
                    finalLocation = namespace + "/" + finalLocation;
                } else {
                    finalLocation = "/" + finalLocation;
                }
            }

            // if the URL's are relative to the servlet context, append (prepend) the servlet context path to finalLocation
            if (prependServletContext && (request.getContextPath() != null) && (request.getContextPath().length() > 0)) {
                finalLocation = request.getContextPath() + finalLocation;
            }

MG2>
//where the session *should* be accessed
// public Map<String, ResultConfig> getResults()
//i see ResultConfig here but no reference to session?
//where in ResultConfig The Builder class I see
 /**
     * The builder for this object.  An instance of this object is the only way to construct a new instance.  The
     * purpose is to enforce the immutability of the object.
     * The methods are structured in a way to support chaining.
     * After setting any values you need, call the {@link #build()} method to create the object.
     */

MG2>

            ResultConfig resultConfig = invocation.getProxy().getConfig().getResults().get(invocation.getResultCode());

            if (resultConfig != null) {
                Map<String, String> resultConfigParams = resultConfig.getParams();

                List<String> prohibitedResultParams = getProhibitedResultParams();
                for (Map.Entry<String, String> e : resultConfigParams.entrySet()) {
                    if (!prohibitedResultParams.contains(e.getKey())) {
                        Collection<String> values = conditionalParseCollection(e.getValue(), invocation, suppressEmptyParameters);
                        if (!suppressEmptyParameters || !values.isEmpty()) {
                            requestParameters.put(e.getKey(), values);
                        }
                    }
                }
            }

            StringBuilder tmpLocation = new StringBuilder(finalLocation);
            urlHelper.buildParametersString(requestParameters, tmpLocation, "&");

            // add the anchor
            if (anchor != null) {
                tmpLocation.append('#').append(anchor);
            }

            finalLocation = response.encodeRedirectURL(tmpLocation.toString());
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug("Redirecting to finalLocation " + finalLocation);
        }

        sendRedirect(response, finalLocation);
    }
MG2>

MG2>i found a testcase for redirect
public class ServletActionRedirectResultTest extends StrutsInternalTestCase {

    public void testIncludeParameterInResultWithConditionParseOn() throws Exception {

        ResultConfig resultConfig = new ResultConfig.Builder("", "")
            .addParam("actionName", "someActionName")
            .addParam("namespace", "someNamespace")
            .addParam("encode", "true")
            .addParam("parse", "true")
            .addParam("location", "someLocation")
            .addParam("prependServletContext", "true")
            .addParam("method", "someMethod")
            .addParam("param1", "${#value1}")
            .addParam("param2", "${#value2}")
            .addParam("param3", "${#value3}")
            .addParam("anchor", "${#fragment}")
            .build();



        ActionContext context = ActionContext.getContext();
        ValueStack stack = context.getValueStack();
        context.getContextMap().put("value1", "value 1");
        context.getContextMap().put("value2", "value 2");
        context.getContextMap().put("value3", "value 3");
        MockHttpServletRequest req = new MockHttpServletRequest();
        MockHttpServletResponse res = new MockHttpServletResponse();
        context.put(ServletActionContext.HTTP_REQUEST, req);
        context.put(ServletActionContext.HTTP_RESPONSE, res);


        Map<String, ResultConfig> results=  new HashMap<String, ResultConfig>();
        results.put("myResult", resultConfig);

        ActionConfig actionConfig = new ActionConfig.Builder("", "", "")
                .addResultConfigs(results).build();

        ServletActionRedirectResult result = new ServletActionRedirectResult();
        result.setActionName("myAction");
        result.setNamespace("/myNamespace");
        result.setParse(true);
        result.setEncode(false);
        result.setPrependServletContext(false);
        result.setAnchor("fragment");
        result.setUrlHelper(new DefaultUrlHelper());

        IMocksControl control = createControl();
        ActionProxy mockActionProxy = control.createMock(ActionProxy.class);
        ActionInvocation mockInvocation = control.createMock(ActionInvocation.class);
        expect(mockInvocation.getProxy()).andReturn(mockActionProxy);
        expect(mockInvocation.getResultCode()).andReturn("myResult");
        expect(mockActionProxy.getConfig()).andReturn(actionConfig);
        expect(mockInvocation.getInvocationContext()).andReturn(context);
        expect(mockInvocation.getStack()).andReturn(stack).anyTimes();

        control.replay();
        result.setActionMapper(container.getInstance(ActionMapper.class));
        result.execute(mockInvocation);
        assertEquals("/myNamespace/myAction.action?param1=value+1&param2=value+2&param3=value+3#fragment", res.getRedirectedUrl());
MG2>in this case a HTTPGet with appended params..but no session vars?

Regards.

MG2>am i reading the code incorrectly?
MG2>Regards

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: [S2] ExecuteAndWait Interceptor // Only re-submit token parameters on refresh?

Yasser Zamani-2


On 4/18/2018 5:27 PM, Martin Gainty wrote:
> MG2>some confusion on where session is accessed
>  * available. This is because actions are built on a single-thread model. The
>  * only way to pass data is through the session
> MG2>with chain interceptor

No it didn't mean "with chain interceptor" that you have added. It meant
the action1.execute (i.e. the Struts user him/herself) can store data in
session and can then retrieve them inside action2.execute after
redirect. It didn't mean Strut's RedirectAction result does these itself
i.e. access, read and pass all session data.

> MG2>Here is code that *should access session params* to pass to new finalLocation (but session is not accessed?)

As mentioned above, we didn't mean RedirectAction result access, read
and pass all session data. The Struts user him/herself should store them
in action1.execute then redirect then access and read them in
action2.execute.

> MG2>
> //where the session *should* be accessed
> // public Map<String, ResultConfig> getResults()
> //i see ResultConfig here but no reference to session?

(Same as above)

> MG2>i found a testcase for redirect
> MG2>in this case a HTTPGet with appended params..but no session vars?

(Same as above)

> MG2>am i reading the code incorrectly?

No you're right that there is no session access but as mentioned above,
it's OK. There is no need to pass session data. They're always available
at server side.

Regards.

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]