Avoiding creating the same object twice on refresh in JSF - jsf

Say we have an "add new object" form in AddObject.jspx, and a Confirm.jsp page (saying something like object with id NN was added to the database...)
in faces-config.xml:
<navigation-rule>
<from-view-id>/AddObject.jsp</from-view-id>
<navigation-case>
<from-outcome>add</from-outcome>
<to-view-id>/Confirm.jsp</to-view-id>
</navigation-case>
</navigation-rule>
So the user opens AddObject.jsp, fills out the form, and clicks the 'add' button that triggers an event handler in the AddObject's backing bean. The backing bean inserts the new object into the database and returns the "add" outcome. So we end up in the Confirm screen.
Now if I hit F5 (refresh the browser) a new object will be created and the Confirm screen redisplayed with the new ID of the new object. I can keep hitting F5 and get as many new objects as I'd like.
I don't want this. I want to detect such situations. Avoid adding the same object many times over (generating many rows in the database that differ only by ID).
Can I do this with JSF? Why does it ask me for from-view-id/outcome/to-view-id if it doesn't seem to make any good use of this information?

There are two parts to this problem.
First, you need to avoid having the broswer's refresh of the confirm page resubmit the request. So one approach if for the brower's history not to say
POST this request
and instead say
GET this confirmation page
We cam do that by using the
<redirect/>
statement in the JSF navigation. Now this means that a redirect response is sent to the Browser. The browser then ask JSF for the conformation page. NOTE: this is now a new request from JSF's perspective, so any data you want to display needs to be at session scope not request scope.
This "Use a redirect for actions that make changes" approach is quite a common idiom. However it doesn't address the "Quick Fire User" problem. What would happen if the user hit the "Submit" twice very rapidly? You can get to a situation where the browser has sent two requests. There are other scenarios where this can happen - network glitch on the first request, user gets impatient, sends the request again.
So we need somehow to handle such double submissions. This is not a JSF-specific problem. A typical solution is for the backend to send a token when the original request page is populated. This is held in a hidden field. When the request is submitted that token (some unique number) is sent back to the application which records that it has been used, hence a double submission is detected.
You can also use JavaScript in the browser to prevent double submission. This should give a friendlier user experience but I would not recommend relying only on Browser-side control. The server should have ultimate respsonbility.

Related

Xpages document changing document mode?

I have a strange thing occurring; as usual, I can't post code, unfortunately, so I'm describing the problem in case anyone can suggest a possible cause.
I have an xpage with a custom control included on it; the custom control handles document locking and changing to edit/read-only modes via links. The document locking is done by setting an applicationScope variable based on the UNID. To make it more friendly for other users on the system, I run a function periodically on the page to check whether the document is locked or not and update a link/label/tooltips appropriately (e.g. if locked by another user, then the "Edit" button is disabled; when the lock is released, it's re-enabled). This is done by calling an "xagent" through a standard, simple dojo-based ajax call.
For some reason, the behavior of the system gets erratic after 45 seconds to a minute. I'm checking the lock status every ten seconds or so, so it's not happening with the first call. I'm displaying a list of records associated with the document; each record is a row in a repeat. When I first go into edit mode, the controls are all displayed as they should be, i.e. editable. If the user changes a particular value with a combobox, it updates the whole row with a partial refresh. When things get erratic, I noticed that the row starts refreshing in read-only mode, which suggests to me that the document is changing edit mode. The only time I knowingly change edit mode is if a "Cancel" or "Save" button is pressed. (The locking mechanism itself doesn't have anything to do with the edit mode.)
It certainly seems like the ajax call I'm making is at the root of this. But I've stripped the xagent and the client-side code down to practically nothing, and it's still happening. I can't see what would be causing this behavior. Can anyone hazard a guess? Thanks....
Maybe check if the server log file has warnings like:
WARNING CLFAD####W: State data not available for /page because no control tree was found in the cache.
If you're seeing those warnings, it could be that the server can no longer find the current XPage page instance in the cache. In that case the page will revert to the initial state, like when the page was first opened. That might be why the document goes to read-only mode.
The session cache of server-side page instances only holds 4 pages when xsp.persistence.mode=basic, or it holds 16 instances when xsp.persistence.mode=file or fileex.
If you load 4 xagent page instances, then that will fill the cache, and it will no longer be able to find the page instance for the current XPage you are viewing. So the XPage will stop performing server-side actions, and partial refresh will always show the initial state of that area of the page.
To avoid that problem, in the xagent page you can set viewState="nostate" on the xp:view tag, so that page instances are not saved for the xagent page, as described here:
https://tobysamples.wordpress.com/2014/12/11/no-state-no-problem/
Or else you can create and reuse one page instance for the xagent, so only one is created. That is, on the first call to the XAgent, have the xagent return the $$viewid value for the xagent page instance (#{javascript:view.getUniqueViewId()}), and then in subsequent requests to the xagent use that $$viewid in the request, to restore the existing xagent page instance instead of creating new instances that will fill the cache. So the subsequent xagent requests will be like so:
/myApp.nsf/xagent1.xsp?$$viewid=!aaaaaaaa!
It's hard to troubleshoot without code, but here are a few thoughts:
How are you checking document locking? Via a client-side JavaScript AJAX call or an XPages partial refresh? If the latter, what is the refresh area? If the former, what is the refresh area you're passing and the return HTML? Does it always occur when you're in edit mode on a row and the check happens, or independently of that? The key thing to check here is what the check for locking is doing - is it checking the server and returning a message outside the repeat, or checking the server and returning HTML that overwrites what's currently on the browser with defaults, e.g. the document mode as read mode.
What network activity is happening between the browser and the server and when? Is something else overwriting the HTML for the row, so resetting the row to read mode.
It's unlikely to be random, the key is going to be identifying the reproduceable steps to identify a common scenario/scenarios and cause.
EDIT
Following on from your additional info, is there a rendered property on the Edit link? If that calculates to false in earlier JSF lifecycle phases, the eventHandler is not available to be triggered during the Invoke Application phase. Because the eventHandler also includes the refreshId, there is no refreshId and refreshMode, so it defaults to a full refresh with no SSJS running. See this blog post for clarification http://www.intec.co.uk/view-isrenderingphase-and-buttons/.

Pure Java/JSF implementation for double submit prevention

We're using JSF 2.0 on WebSphere v8.5 with several component libraries PrimeFaces 4.0, Tomahawk 2.0, RichFaces, etc.
I am looking for generic mechanism to avoid form re-submission when the page is refreshed, or when the submit button is clicked once again. I have many applications with different scenarios.
For now I have considered disabling the button with a piece of JavaScript in onclick attribute, but this is not satisfying. I'm looking for a pure Java implementation for this purpose, something like the Struts2 <s:token>.
I am looking for generic mechanism to avoid form re-submission when the page is refreshed
For that there are at least 2 solutions which can not be combined:
Perform a redirect after synchronous post. This way the refresh would only re-execute the redirected GET request instead of the initial request. Disadvantage: you can't make use of the request scope anymore to provide any feedback to the enduser. JSF 2.0 has solved this by offering the new flash scope. See also How to show faces message in the redirected page.
Perform the POST asynchronously in the background (using ajax). This way the refresh would only re-execute the initial GET request which opened the form. You only need to make sure that those forms are initially opened by a GET request only, i.e. you should never perform page-to-page navigation by POST (which is at its own already a bad design anyway). See also When should I use h:outputLink instead of h:commandLink?
or when the submit button is clicked once again
For that there are basically also at least 2 solutions, which could if necessary be combined:
Just block the enduser from being able to press the submit button during the submit and/or after successful submit. There are various ways for this, all depending on the concrete functional and design requirements. You can use JavaScript to disable the button during submit. You can use JSF's disabled or rendered attributes to disable or hide the button after submit. See also How to do double-click prevention in JSF 2. You can also use an overlay window during processing ajax requests to block any enduser interaction. PrimeFaces has <p:blockUI> for the purpose.
Validate uniqueness of the newly added entity in the server side. This is way much more robust if you absolutely want to avoid duplication for technical reasons rather than for functional reasons. It's fairly simple: put a UNIQUE constraint on the DB column in question. If this constraint is violated, then the DB (and DB interaction framework like JPA) will throw a constraint violation exception. This is best to be done in combination with a custom JSF validator which validates the input beforehand by performing a SELECT on exactly that column and checking if no record is returned. A JSF validator allows you to display the problem in flavor of a friendly faces message. See also among others Validate email format and uniqueness against DB.
Instead of creating a token manually, you can use BalusC' solution. He proposed a Post-Redirect-GET pattern in his blog
Alternative solutions can be found in these answers:
Simple flow management in Post-Redirect-Get pattern
How can Flash scope help in implementing the PostRedirectGet (PRG) pattern in JSF2.0
<!--Tag to show message given by bean class -->
<p:growl id="messages" />
<h:form>
<h:inputText a:placeholder="Enter Parent Organization Id" id="parent_org_id" value="#{orgMaster.parentOrganization}" requiredMessage="Parent org-id is required" />
<h:commandButton style="margin-bottom:8px;margin-top:5px;" class="btn btn-success btn-block " value="Save" type="submit" action="#{orgMaster.save}" onclick="resetform()" />
</h:form>
public String save() {
FacesContext context = FacesContext.getCurrentInstance();
context.getExternalContext().getFlash().setKeepMessages(true); //This keeps the message even on reloading of page
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Your submission is successful.", " ")); // To show the message on clicking of submit button
return "organizationMaster?faces-redirect=true"; // to reload the page with resetting of all fields of the form.. here my page name is organizationMaster...you can write the name of form whose firlds you want to reset on submission
}

Xpages partial refresh trigger full refresh

Trigger full refresh when logged out in 8.5.3. I don't care if the data is lost, I just want to trigger full refresh if partial refresh fails in case user is logged out or session has timed out.
I have two windows open. If I press the logout button on window 2, and then come back onto window 1 and try to do a partial refresh, it returns nothing (just blank value for that control). So how can I trigger a full refresh when the user is logged out?
I tried onComplete, onStart and onError client side events, but cannot figure out how I could find out when this logout has happened, I tried the following code:
var strUserName = '#{javascript: #Name("[CN]", #UserName());}';
alert("Start: " + strUserName);
It always returns the username rather than anonymous in window 1, even when user is logged out in window 2!
onError event never fires when partial refresh fails or returns blank value.
Any ideas or help would be greatly appreciated.
Did you try to lock the database down (Anonymous = NoAccess)? Then you should get an 404 and the error routine fires.
Update
You can:
add a redirect meta header to the page that kicks in when a session expires (cheapo solution, but might catch already most used cases)
Still lock down the database but give Anonymous the right "view public pages" and then have the page(s) that anonymous is allowed to see marked with the attribute "available for public access"
Make sure your partial refresh checks if nothing comes back and reloads the page then (most work since you need to alter business logic) - some sample of your code would be needed
onComplete has one weakness. The value bindings are evaluated when the page is rendered, not after a partial refresh, as you might think. This is due to the way the request works. When a request is sent, the callbacks for the request are bound (onStart,onComplete, etc.). Therefore the client side handler code has to be available before the refresh happens.
If the partial refresh doesn't refresh the item with the event handler(onComplete/onStart/onError), I think the value binding is only updated on page load.
There are several ways to solve this.
Have a client side function that does partial refresh on a hidden (by CSS) xp:text/etc. that shows the current user name. Fetch the content using theNodeReferenceVariable.innerHTML. If Anonymous is the current user:
document.location.href = document.location.href
You could also do document.location.reload(), but that might result in submits being resent.
Use the XPages Extension Library REST services. Same as above, if Anonymous, reload page.

Dealing with Back Button and Resulting Phase_ID

I'm in the following situation:
JSF (MyFaces 2.1.7 and RichFaces 3.3.3)
A session Scoped Bean TEST_PAGE
Users who will use BACK button to no end
State Saving on Server
Scenario:
User click a link to navigate to page with Session Scoped bean (TEST_PAGE)
User clicks another link
User presses back button of brwoser which puts them back in TEST_PAGE
User Tries to Search in this page
On search the backing bean throws exceptions since there are many null values. Reason being is that when they pressed "Search", the page is in RESTORE_VIEW phase.
I tried disabling caching on the page, to force user to refresh the page on back button, but backlash caused by many users complaining "why can't I use back button, I could do it before!" and resulted in Help Desk Tickets since they think the application is crashing on "Confirm Form Resubmission" page chrome/IE/Firefox show, hence managers want me to look for alternatives.
My questions:
It is possible to detect the current Phase_ID of RESTORE_VIEW and not do any processing in this phase to avoid the exceptions, but that alone and just retuning gives user a partial page view. Is it possible to put the page in RENDER_RESPONSE in this scenario and do the processing then? This should refresh the page.
Question Part 2
Since the recommendation (or common/good) practive is to disable cache on dynamic pages i have done so and will have to re-educate the stubborn users.
However i have the following issue.
When pressing Back button on a page with disabled cache, the browser shows the "Confirm Form Resubmission" (chrome) where users either can press Refresh / F5 or Enter key.
If Refresh/ F5 is the action, everything is ok where i detect RESTORE_VIEW phase and avoid any processing in that PHASE and let the processing be done in RENDER_RESPONSE phase. However if Enter is pressed, The PhaseID is in RENDER_RESPONSE(6) but values of few drop-down controls is null which causes the page to fail. So i put in checks to detect such conditions and show a FaceMessage to tell the user to Refresh the page.
Question is, Can i force the page to Refresh instead of forcing the user to do it? The Navigation case for this page is in format:
<navigation-rule>
<navigation-case>
<from-outcome>/app/ord/r1</from-outcome>
<to-view-id>/jsp/ordersHistory.jsp</to-view-id>
</navigation-case>
</navigation-rule>
I've seen code snippets online where faces-redirect=true is used to force reload a page but i couldn't get that to work! Any suggestions?
Disabling the browser cache on dynamic pages is the way to go.
As to the new problems caused after this, this is likely caused by poor webapp design. Redesign your webapp to use GET instead of POST for plain vanilla page-to-page navigation (i.e. use just <h:link> or <h:outputLink> instead of <h:commandLink>). Implement the POST-Redirect-GET pattern for POST requests which needs to send the response to a different page, or change them to return to the same page and conditionally show the results, if possible by ajax. Check if some forms can't better be GET forms (for example, search forms like Google). Rethink if that bean really needs to be a session scoped one, you usually use the session scope for logged-in user and its preferences only, not for request or view scoped data.
See also:
When should I use h:outputLink instead of h:commandLink?
How to choose the right bean scope?
How to avoid re-execution of last form submit action when the page is refreshed?
Is JSF 2.0 View Scope back-button safe?

Seam:token tag not being respected

When I click a command button, and then hit the browser back button to the form and click it again, it submits a second time without throwing the proper exception...
Even stranger, the form id itself is DIFFERENT when I come back, which implies it has regenerated a "valid" form id at some point.
Here's the relevant code: Any ideas?
<h:form id="accountActivationForm">
<s:token/>
<a4j:commandButton id="cancelActivateAccountButton"
action="#{controller[cancelAction]}"
image="/images/button-Cancel-gray.gif"
reRender="#{reRenderList}"
oncomplete="#{onCancelComplete}" />
 
<a4j:commandButton id="activateAccountButton"
action="#{controller[agreeAction]}"
image="/images/button-i-agree-continue.gif"
styleClass="activate-account-button"
reRender="#{reRenderList}"
oncomplete="#{onActivationComplete}"/>
</h:form>
Clarifications:
I inherited this, so I'm trying to change it as little as possible. (It's used in a couple places.)
Each action returns a view, not null. I have confirmed this by stepping through line-by-line.
The reRenderList is empty in my current test-case.
onActivationComplete is also empty.
I'm going to be going template-by-template to see if someone made it with nested forms, because my coworkers have had unrelated problems due to that, so it couldn't hurt to eliminate that as a possible problem.
The s:token is supposed to avoid double/multiple submits by impatiently clicking the submit button multiple times in the same request or by refreshing the non-redirected result in the webbrowser or by resubmitting the cached page in the browser history.
That it works when the client navigates back and forth by browser history just means that the pages with the forms are not cached in client's browser history and are requested as brand new from the server side again. That would indeed return a new token. Check it yourself with a HTTP tracker like the one in Firebug.

Resources