2

I hope someone can help me solve a very serious problem we face at the moment with a business critical application losing data when a user works in it.

This happens randomly - I have never reproduced this but the users are in the system a lot more than me.

A document is created with a load of fields on it, and there are 2 rich text fields. We're using Domino 8.5.3 - there are no extension lib controls in use. The document has workflow built in, and all validation is done by a SSJS function called from the data query save event. There is an insane amount of logging to the sessionscope.log and also this is (now) captured for each user in a notes document so I can review what they are doing.

Sometimes, a user gets to a workflow step where they have to fill in a Rich Text field and make a choice in a dropdown field, then they submit the document with a workflow button. When the workflow button is pressed (does a Full Update) some client side JS runs first

// Process any autogenerated submit listeners
if( XSP._processListeners ){ // Not sure if this is valid in all versions of XPages
    XSP._processListeners( XSP.querySubmitListeners, document.forms[0].id );
}

(I added this to try and prevent the RTF fields losing their values after reading a blog but so far it's not working)

then the Server-side event runs and calls view.save() to trigger QS code (for validation) and PS code to run the workflow agent on the server.

95% of the time, this works fine.

5% of the time however, the page refreshes all the changes made, both to the RFT field (CKEditor) and the dropdown field are reloaded as they were previously, with no content. It's like the save hasn't happened, and the Full Update button has decided to work like a page refresh instead of a submit.

Under normal circumstances, the log shows that when a workflow button is pressed, the QuerySave code starts and returns True. Then the ID of the workflow button pressed is logged (so I can see which ones are being used when I am reviewing problems), then the PostSave code starts and finally returns true.

When there is a problem, The QuerySave event runs, returns true if the validation has passed, or false if it's failed, and then it stops. The ID of the workflow button is also logged. But the code should continue by calling the PostSave function if the QuerySave returns true - it doesn't even log that it's starting the PostSave function.

And to make matters worse, after the failure to call the PostSave code, the next thing that is logged is the beforePageLoad event running and this apparently reloads the page, which hasn't got the recent edits on it, and so the users loses all the information they have typed!

This has to be the most annoying problem I've ever encountered with XPages as I can find no reason why a successful QuerySave (or even a failure because mandatory fields weren't filled in) would cause the page to refresh like this and lose the content. Please please can someone help point me in the right direction??

Declan Lynch
  • 3,345
  • 17
  • 36
asummers
  • 177
  • 1
  • 15
  • There is more to this question now - the 5% of docs that lose their value is easily reproduced when you know what the users are doing. Can you have a look at this question: http://stackoverflow.com/questions/12198351/xpages-same-document-in-multiple-windows – asummers Aug 30 '12 at 13:51

4 Answers4

1

It sounds as if in the 5% use cases, the document open for > 30mins and the XSP session is timing out - the submit causes the component tree to be re-created, and the now empty page returned back to the user. Try increasing the time out for the application to see if the issue goes away.

Jeremy Hodge
  • 1,655
  • 9
  • 8
  • Thanks for the reply. I wish it was that simple though. I have the code version of KeepSessionAlive (instead of the ExtLib CC) on the page, and this can happen, as it did today, after the user has successfully submitted the document through a number of previous workflow steps and then got to one where a RTF field needs to be filled in. Today's problem saw the same document submitted and reloaded through the workflow 3 times before the problem happened, each time the page being subjected to a "Full Update"; all in the space of about 3 minutes. – asummers May 31 '12 at 15:09
  • I assume that a full update on the document that DOES work properly would cause the session timeout to be restarted though right? So if it's 30 mins and someone submits after 15, they get another 30 mins from that point? – asummers May 31 '12 at 15:35
  • Thanks for all the help and pointers so far. I realised after the discussion about managed beans being used for proper validation - there's a good reason as far as I can tell why this can't be done. We needed to have tabbed panels on the document and the validation on fields only runs on fields you can see. That's why the validation was moved to a script lib to run against the whole document when submitted. – asummers Aug 30 '12 at 13:50
0

I would design the flow slightly different. In JSF/XPages validation belongs into validators, not into a QuerySave event. Also I'd rather use a submit for the buttons, so you don't need to trigger a view.save() in code. This does not interfere with JSF's sequence of things - but that's style not necessarily source of your problem.... idea about that:

As Jeremy I would as a first stop suspect a timeout, then the next stop is a fatal issue in your QuerySave event, that derails the runtime (for whatever reason). You can try something like this:

var qsResult = false;
// your code goes here, no return statements
// please and if you are happy
qsResult = true;
return qsResult;

The pessimistic approach would eventually tell you if something is wrong. Also: if there is an abort and your querySave just returns, then you might run in this trap

function noReturn() {return; }  //nothing comes back!

noReturn() == true;    --> false
noReturn() == false;   --> false
noReturn() != false;   --> true!!!!

What you need to check: what is your performance setting: serialize to disk, keep in memory or keep latest in memory? It could be you running foul of the way JavaScript libraries work.

A SSJS library is loaded whenever it is needed. Variables inside are initialized. A library is unloaded when memory conditions require it and all related variables are discarded. so if you rely on any variable in a JS Function that sits inside a SSJS library between calls you might or might not get the value back, which could describe your error condition. Stuff you want to keep should go into a scope (viewScope seems right here).

To make it a little more trickier: When you use closures and first class functions these functions have access to the variables from the parent function, unless the library had been unloaded. Also functions (you could park them in a scope too) don't serialize (open flaw) so you need to be careful when putting them into a scope.

If your stuff is really complex you might be better off with a backing bean. Did that help?

stwissel
  • 20,110
  • 6
  • 54
  • 101
  • Wow, thanks for all the handy tips here. One or two things to note with my design that may or may not make a difference. The app has around 100 fields on any of the normal workflow docs that get submitted, with more and more info being added as it goes through the flow. It has a "field" document for each of these fields that describes the label for the field, the help text that gets added to a tooltip, and then whether it's mandatory or not (and mandatory field error message). when I took over this app, I removed the validators as there was a DBLookup for each field and put it in a script – asummers Jun 01 '12 at 06:54
  • The script gets called from the QS event and does all the validation by getting these Field documents as a collection and then going through each one in turn, rather than 100 DbLookups to see if each field is mandatory etc. Not sure if this approach is better given the requirement to be able to change details about the fields (this app is rolled out with the same design a few times now). – asummers Jun 01 '12 at 06:58
  • And finally, to combat this bug : [link]http://www-304.ibm.com/support/docview.wss?uid=swg1LO55614 I have the app set to "Keep only the current page in memory" – asummers Jun 01 '12 at 06:59
  • So basically you have a validation mechanism that is described in configuration documents. I still would use a validator and not a QS event. Create a managed bean (that one yells for an applicationScope) that loads your validation definitions in memory. Create a custom validator in Java that talks to the bean. Then you only need to assign the validator to your fields. What type of validation conditions do you record in your configuration? – stwissel Jun 01 '12 at 11:35
  • the main type of validation is just to check that a field isn't empty. But certain fields become mandatory at certain steps in the workflow. Like you don't need to fill in the solution to the problem when you are submitting the problem, kind of thing. Also - you said about setting a var qsResult = false; and then setting it to true and returning that in the QS if you are happy. I am already doing this, and the QS definitely finished the last time there was an error as the log shows (31-May-2012 08:53:06)... caseQuerySave Returning: true (31-May-2012 08:53:06)... caseQuerySave :- Finished – asummers Jun 01 '12 at 13:30
  • The code that calls all this from the WF buttons looks like this: – asummers Jun 01 '12 at 13:33
  • if (caseQuerySave(currentDocument)) { – asummers Jun 01 '12 at 13:35
  • if (casePostSave(currentDocument)){ – asummers Jun 01 '12 at 13:35
  • The log showed the end of the caseQuerySave function returning true, then "WF Button 2", then it started on the beforePageLoad event. Like the save and the postSave function never ran – asummers Jun 01 '12 at 13:36
  • OK so I am following this article to add a managed bean (I've already got a bean for capturing details about the user, what company they are in, contact details etc.) [link](http://xsnippets.info/XSnippets.nsf/snippet.xsp?id=intermediate-custom-input-validation-failover-during-process_validations-phase) – asummers Jun 01 '12 at 14:50
  • How would I make this an application scope bean though - do you mean the bean should init by going and getting a list of the mandatory fields and their error message and stick that in the app scope, then when the Validate function is called, check if the component is one of the mandatory fields? – asummers Jun 01 '12 at 14:53
0

To create a managed bean (or more) check Per's article. Your validator would sit in a application bean:

<faces-config>
    <managed-bean>
       <managed-bean-name>workflowvalidator</managed-bean-name>
       <managed-bean-class>com.company.WfValidator</managed-bean-class>
       <managed-bean-scope>application</managed-bean-scope>
    </managed-bean>
</faces-config>

Inside you would use a map for the error messages

public Map<String,String> getErrorMessages() {
     if (this.errorStrings == null) { // errorStrings implements the MAP interface
        this.loadErrorDefinitions(); //Private method, loads from Domino
     }
     return this.errorStrings;
}

then you can use EL in the Error message string of your validators:

 workflowvalidator.errorMessage("some-id");

this allows XPages to pick the right one directly in EL, which is faster than SSJS. You could then go and implement your own custom Java validator that talks to that bean (this would allow you bypass SSJS here). Other than the example I wouldn't put the notes code in it, but talk to your WfValidator class. To do that you need to get a handle to it in Java:

private WfValidator getValidatorBean() {
    FacesContext fc = FacesContext.getCurrentInstance();
    return (WfValidator) fc.getApplication()
                           .getVariableResolver()
                           .resolveVariable(fc, "workflowvalidator");
}

Using the resolver you get access to the loaded bean. Hope that helps!

stwissel
  • 20,110
  • 6
  • 54
  • 101
  • Thanks for all the valuable info above. I have begun writing the Java bean validator and got a simlpe version working. I am now going to wait for the new Java developer to join my team next month so he can finish it off :-) – asummers Jun 12 '12 at 15:31
  • One thing I did just notice though is that if you have Concurrency Mode set to Fail so rep conflicts aren't created, and something edits a document while you are working on the web, the query save code runs when you try and save but immediately stops execution when the actual save is called. I described above how the QS ran and then instead of doing the save and then running the PS event code, the next thing that ran was the beforePageLoad event. It seems that my code to handle the error if there is a problem saving is completely ignored when Concurrency Mode kicks in – asummers Jun 12 '12 at 15:34
0

My experience is that this problem is due to keeping page in memory. Sometimes for some reason the page gets wiped out of memory. I'm seeing this when there is a lot of partial refreshes with rather complex backend Java processing. This processing somehow seems to take the space from memory that is used by the XPage.

The problem might have been fixed in later releases but I'm seeing it at least in 8.5.2.

In your case I would figure out some other workaround for the CKEditor bug and use "Keep pages on disk" option. Or if you can upgrade to 9.0.1 it might fix both problems.

Panu Haaramo
  • 2,932
  • 19
  • 41