ADF, PrettyFaces and a persistent query param

Splash Forums PrettyFaces Users ADF, PrettyFaces and a persistent query param

This topic contains 17 replies, has 3 voices, and was last updated by  clemmonsm 10 years, 2 months ago.

Viewing 15 posts - 1 through 15 (of 18 total)
  • Author
    Posts
  • #18058

    clemmonsm
    Participant

    I have a PrettyFaces-Oracle ADF integration challenge with which I am hoping someone can help me.

    In full disclosure, I have posted an app specific version of this same challenge here: https://forums.oracle.com/forums/thread.jspa?messageID=9944747

    In a nutshell, ADF produces a persistent query parameter in the URL – “_adf.ctrl-state” – that is used as a mechanism for tracking the current session and current page instance. Without it, the back button on some browsers will fail, sending the user to the content root. Typically, the value for this parameter will be the same across page requests so the server can track the user’s session. With PrettyFaces, this value is reinitialized on each page load because – I believe – ADF sees the page request as a new session.

    I am curious if anyone here has dealt with a similar issue in their apps – whether specific to ADF or not – and if they discovered a viable solution to persisting a query parameter value across a session in PrettyFaces. I am resigned to the fact that this query parameter will be hanging on at the end of my pretty URLs, so that’s fine, however, I would like to find a solution that did not involve defining this query parameter in the pattern or my url-mappings.

    One additional item I would like to note from the documentation regarding PF request processing: “PrettyFaces relinquishes control of the current request via RequestDispatcher.forward(“/context/faces/viewId.jsf”).” Would it be possible to pass this parameter through to ADF by including it in the forward?

    Thanks for reading.

    I am using JSF1.2, ADF 11.1.1.5 & PrettyFaces 3.3.0.

    #21568

    Hey clemmonsm,

    a very interesting issue. Actually I thought that the original query parameters are preserved by PrettyFaces if you access a mapping. As far as I can tell PrettyFaces simply wraps the HttpServletRequest and just adds query parameters to the existing ones.

    Could you perhaps test this? You could simply choose some action method and manually read the query parameters from the HttpServletRequest to check if the parameter is still there.

    Another possible cause of this issue is that PrettyFaces also wraps the HttpServletResponse and hooks into encodeURL(). I think ADF also does this to add this special query parameter to the generated URLs. Perhaps this isn’t working correctly if both frameworks modify the URL? Perhaps the order is important?

    Christian

    #21569

    clemmonsm
    Participant

    Hi Christian,

    Thank you for the reply.

    I was able to obtain the param value from HttpServletRequest and append it to the target url like so:

    String targetURL = context.getContextPath() + builder.build(mapping, true, params)
    + "?_adf.ctrl-state=" + request.getParameterValues("_adf.ctrl-state")[0];

    Knowing this, it appears as though my best bet for navigation throughout the site will be to use Java redirects since pretty:link and PF command actions will not persist the ADF param.

    Can you think of any customization I could perform that would allow the use of pretty:link and PF command actions in this scenario?

    Again, thank you for your insight.

    #21570

    @clemmonsm

    You could implement a custom rewrite Processor, and do the appending of the parameter there, so that it is never lost (careful, Processors work on all URLs, so you may need to do some checking.)

    http://ocpsoft.com/docs/prettyfaces/3.3.0/en-US/html/inbound_rewriting.html#inbound_rewriting.options

    (The Interface has changed slightly to be more useful ;)

    ~Lincoln

    #21571

    clemmonsm
    Participant

    Ah ha, this sounds promising. I will work on this and report back… Thanks.

    #21572

    clemmonsm
    Participant

    Hey guys,

    I have had a chance to implement a custom rewrite processor. Unfortunately, it appears that when the processor runs, ADF has already re-initialized the “_adf.ctrl-state” query param value, so I no longer have access to the prior value which I need to append to the URL. I am perplexed because I expected the query param value to not yet be altered by ADF since the redirect has not taken place. Any enlightenment you can provide would be much appreciated. Thanks!

    Here is my config and code…

    <!-- For URLs containing ".jspx", utilize processor -->
    <rewrite match=".*(.jspx)+.*" processor="siteUtils.PrettyFacesAdfProcessor" redirect="301" />

    package siteUtils;

    import com.ocpsoft.pretty.faces.config.rewrite.RewriteRule;
    import com.ocpsoft.pretty.faces.rewrite.Processor;

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    public class PrettyFacesAdfProcessor implements Processor {

    public String processInbound(final HttpServletRequest request, final HttpServletResponse response, final RewriteRule rewrite, final String url) {
    // do nothing
    return url;
    }

    public String processOutbound(final HttpServletRequest request, final HttpServletResponse response, final RewriteRule rewrite, final String url) {

    if (request.getParameterValues("_adf.ctrl-state") != null && !url.contains("_adf.ctrl-state")) {
    // param exists and is not assigned to url
    // Unfortunately, it appears the query value in the request has been altered at this point in the lifecycle

    // append _adf.ctrl-state key/value to query string
    String newUrl = url + "?_adf.ctrl-state=" + request.getParameterValues("_adf.ctrl-state")[0];

    return newUrl;
    }

    return url;
    }
    }

    #21573

    Hmm, this must mean that there is something activating before PrettyFaces Filter. You could try manually specifying PrettyFaces to invoke before ADF in web.xml?

    #21574

    clemmonsm
    Participant

    By that do you mean a technique other than having PrettyFaces set as the first filter in web.xml? I am researching to see how this would be done, but wanted to clarify before getting too far off-base is the answer is no…

    #21575

    No, thats what I meant. But I think I actually see your problem.

    public String processInbound(final HttpServletRequest request, final HttpServletResponse response, final RewriteRule rewrite, final String url) {
    // do nothing
    return url;
    }

    You need to capture the original value of the parameter here, to be saved into the request later until you need it when doing the processOutbound() method, otherwise you are correct, ADF would probably have re-initialized it.

    However… I have a sneaking suspicion that this isn’t going to work either. It’s worth a try, however.

    If this doesn’t work, We need to figure out exactly what is going wrong (hopefully you could do a bit of debugging and see what is going on.) Then we can make a better recommendation instead of guessing ;)

    #21576

    Depending on the results of your debugging, we should definitely be able to get this working. We just need to figure out what you need to make it work :) (Pull requests/contributions welcome, also!)

    #21577

    clemmonsm
    Participant

    After a bit of debugging, I do see the original param in the processInbound method; however, I am not getting consistent breaks in that method. For example, when the app loads, the breakpoint hits in processInbound, but when I click a pretty:link I do not get a break. I believe this has to do with the regex I am employing in my redirect match:

    <!-- For URLs containing ".jspx", utilize processor -->
    <rewrite match=".*(.jspx)+.*" processor="siteUtils.PrettyFacesAdfProcessor" redirect="301" />

    So, let me ask this: should my rewrite match regex be targeting the url-mapping pattern or the view-id or both?

    #21578

    clemmonsm
    Participant

    Okay, I have done a bit of debugging and below is what I have found. It’s a lot of information, so hopefully I can make it simple.

    Here is the rewrite match i am using for testing. It excludes images, css, js and some random ADF html and path resources:

    <rewrite match="^(?:(?!.gif|.jpg|.jpeg|.png|.cur|.css|.js|.html|/afr/|#).)*$r?n?" processor="siteUtils.PrettyFacesAdfProcessor" redirect="301" /> <!-- do not match strings with these char strings -->

    I am using a bare-bones custom processor code for this walkthrough with breaks on the return statements.

    public class PrettyFacesAdfProcessor implements Processor {

    public String processInbound(final HttpServletRequest request, final HttpServletResponse response, final RewriteRule rewrite, final String url) {
    return url;
    }

    public String processOutbound(final HttpServletRequest request, final HttpServletResponse response, final RewriteRule rewrite, final String url) {
    return url;
    }
    }

    My test app includes two simple pages, each with a global header that includes two pretty:links.

    Here is the life-cycle I see when debugging the custom processor:

    @ processInbound() is called on the URL “/”. No “_adf.ctrl-state” param exists at this point.

    @ processOutbound() is called twice on the URL ” “

    @ processInbound() is called once on the URL “/?…” where “…” are ADF params, but not “_adf.ctrl-state”.

    @ processOutbound() is called twice on the URL “/?…”. This is the first time the URL has the param “_adf.ctrl-state”

    @ processInbound() is called once on the URL “/?…”

    @ processOutbound() is called twice on the URL “/?…”. This is the first time the request object contains the param “_adf.ctrl-state”

    @ processOutbound() is called twice on the URL “/”.

    @ processOutbound() is called twice for each pretty:link that is placed on the page. Each time, the current ADF “_adf.ctrl-state” param value is available in the request object.

    @ When a pretty:link is selected: processInbound() is called twice for the selected link.

    @@ On the first call, the request object is empty – the previous “_adf.ctrl-state” value is gone. ADF sees this as a redirect from outside the framework.

    @@ As a result, on the second call, ADF generates three params that are now tacked on to the end of the URL. The “_adf.ctrl-state” object has not yet been regenerated.

    @ Next, processOutbound is called a number of times in the following order:

    @@ Six times for the url-mapping that is being loaded. At this point, ADF has reinitialized the “_adf.ctrl-state” param and added it to the URL. The value is in the request object.

    @@ Four times, twice for each of the two pretty:links found the page header

    In your post, you say I “need to capture the original value of the parameter [in the processInbound method], to be saved into the request later until you need it when doing the processOutbound() method.”

    From what I can see, I only have access to the param in the outbound method.

    One fix I tried was to capture the value in processInbound() and save it to session, but access to FacesContext is not available at that time of the lifecycle.

    The next thing I tried, was adding the param to the end of each pretty:link in the processOutbound() method. This allows me to persist the param across pages, but of course, I lose some prettiness because the query param is attached to the end of each pretty:link on the page.

    In the event that the above is a bit too confusing, I would be happy to record a debugging session and share it online.

    #21579

    I think it is important to understand the difference between inbound and outbound rewrites.

    Inbound rewriting happens whenever a request is received and processed by the container. It works on the URL the container receives from the client. It happens very early in the request processing. If a inbound rewrite is of type “chain”, the rewritten request is “forwarded”, which will mean that it is handles as a new requests which will also pass the filter.

    Outbound rewriting happens when the container writes an URL to the resulting HTML page. Therefore outbound rewriting happens very often during request processing, as it has to be done for each link in the output document.

    ADF will propably use a technique similar to outbound rewriting to add the “_adf.ctrl-state” parameter to all links of a rendered HTML page. If the user now clicks on one of those links, the query parameter will be included in the request to the server and ADF will identify the correct context. Therefore the query parameter definitively has to be included even in the pretty links.

    So it is reasonable that you see the “_adf.ctrl-state” parameter in an outbound method first. Initially there is no query parameter in the very first request. So ADF will create one by adding them to the links written to the HTML document.

    I think for you it is important to identify the reason why the query parameter gets lost.

    Lincoln’s idea was to do something like this.

    • In processInbound() retrieve the query parameter and save it somewhere. I think it would make sense to store it as an attribute in the request so you can easily retrieve it later.
    • In processOutbound() you will have to make sure that the query parameter is correctly added to the URL. You can get the original query parameter by reading the attribute from the request.

    But as I mentioned before: The most important point for you is to find the place where the parameter gets lost because in theory the ADF query parameter should work out of the box without any hacks.

    I hope this helps

    Christian

    #21580

    clemmonsm
    Participant

    Hi Christian,

    I appreciate your and Lincoln’s time and patience on this.

    I believe all of your assumptions regarding ADF are correct.

    For my own clarification, what is being said is:

    >> Out of the box, expected behavior is that the ADF “_adf.ctrl-state” query param should be persisted between requests

    >> If a workaround is required, appending the param onto the outbound links inside the processOutbound() method in this manner reasonable

    >> Avoiding this hack is, of course, preferable and determining where/how/why the parameter is being lost is the key to getting it to work as expected

    I will continue digging to determine where the parameter is being lost. I do know that ADF embeds the parameters and their values within the form tag in HTML so they can be used on the next request (see here: http://bit.ly/pYI6N), so maybe that’s a starting point.

    *** One point of clarification on my previous post, the first time the request object contains the param “_adf.ctrl-state” is actually on the third processInbound() and not the subsequent processOutbound(). This means that when rewriting outbound links, the ADF param is available for me in the request object. Regarding your comment on Lincoln’s idea, you say “it would make sense to store it as an attribute in the request so you can easily retrieve it later.” By “later,” I am assuming you mean later in the existing request and not subsequent requests. If so, the param and its value are already available to me when rewriting outbound links and I do not have a need to save the param.

    #21581

    I just had a quick look at the blog post you mentioned. It looks like the _adf.ctrl-state parameter is included in the URL as a query parameter AND is included in the HTML forms, correct?

    Alternatively if the user tries to trick the server by copying the URL from another tab with the _adf.ctrl-state variable in the URL, but obviously missing a payload containing the _adf.ctrl-state form parameter and the Adf-Window-Id parameter, again ADF is smart enough to detect this as a new browser tab and spawns a new PageFlowScope bean.

    A good starting point for you would be to check this:

    1. Do the links rendered to the HTML page contain the ADF parameter? If the parameter is append correctly, the outbound rewriting don’t seem to be a problem.
    2. If 1. works correctly the problem could be caused by the inbound part. If a PrettyFaces URL including the ADF parameter hits the PrettyFilter, the request will be internally forwarded to the JSF view but this should not have any effect on the query parameter and it should still be accessible from the HttpServletRequest

    Regarding your clarification: Yes, with “later” I referred in the processOutbound() method calls for the same request. But you are right. If you still find the ADF parameter in the request, you won’t have to save it in the request.

Viewing 15 posts - 1 through 15 (of 18 total)

The forum ‘PrettyFaces Users’ is closed to new topics and replies.

Comments are closed.