I have an error in my application which only happens when a user double clicks a command link.
What appears to be happening is that the first click correctly does the post then redirect and creates new instances of the WebBeans with the correct view param against them.
The second click tries to restore the old view in the POST, which will nolonger exist so creates a new one and new ViewScoped beans associated. When it comes to reading the view param in the #PostConstruct this is null and an exception is thrown later in the code.
Has anyone experienced something like this? How's best to code around it? I'm trying to avoid detecting and stopping this on the client since I'd like to understand the problem more rather than hiding it behind JS!
Related
sorry if this question has been asked but i couldn't find the answer.
I've converted some spring beans to cdi #Named and set the scope to FlowScoped. I've done this to fix a problem where session scoped beans were shared across multiple tabs and breaking the application in previously opened tabs.
I've got it partly working but i'm running into an issue with the entry point for the FlowScoped beans.
All the examples i have found use a page with a button, this button has an action which navigates to the flow entry point (i've created a blank bean-flow.xml file). is there a way to enter a flow scope directly from a link or from a faces redirect? I kind of need this for two reasons. Or maybe there is another work around? I use spring security to login and it is set with a default-target-url="/search/search.xhtml". I've got around this by creating another page and just using the below to do a redirect on load
ConfigurableNavigationHandler configurableNavigationHandler =
(ConfigurableNavigationHandler) FacesContext.getCurrentInstance()
.getApplication().getNavigationHandler();
configurableNavigationHandler.performNavigation("search");
the above works but the below doesnt
FacesContext.getCurrentInstance().getExternalContext().redirect("/search/search.xhtml");
Idealy i would also like to be able type straight into the address bar
http://localhost:8080/searchApp/search/search.xhtml?searchcriteria=somecriteria
At the moment when i do that or the faces redirect i get an error
javax.servlet.ServletException: WebBeans context with scope type annotation #FlowScoped does not exist within current thread
javax.faces.webapp.FacesServlet.service(FacesServlet.java:659)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
org.apache.openejb.server.httpd.EEFilter.doFilter(EEFilter.java:65)
Any help at this point would be greatly appreciated.
It looks like you are trying to (ab)use a scope for which it was not directly meant to be used. Using the Deltaspike #WindowScoped is what you should use. It creates a scope per browser window or tab
I wanted to test to see if a session scope bean was in fact valid in a beforePageLoad event using SSJS. If I clear the session scopes using the Debug toolbar I get an error when trying to test the bean to see if it is null.
if (registrationBean == null){
The error is:
Error while executing JavaScript action expression
Script interpreter error, line=2, col=22: [ReferenceError] 'registrationBean' not found
Question is how to test the bean to see if it is instantiated??? If not, then I want to go to another XPage (redirect) to allow the user to enter the values that are used to populate the bean.
Or, is this only an issue because I used the Debug Toolbar to clear ALL session and application scopes?
Howard
Use a try/catch block. If you get the error, it's null.
I suspect it's only a problem because the applicationScope has been cleared. Usually the first attempt to reference the variable instantiates the object. However, clearing the whole of applicationScope can break a lot of XPages code in your application and should be avoided.
First, make sure that the bean is set up in faces_config.xml (but it seems this is the case). Then you should not be able to reference the bean before it is instantiated...
An easy way to see when the bean is instantiated is to put a System.out.println("....") in the constructor of the bean. That should bring you on the track. You could (should!) use the OpenNTF LogReader to see what is going on - this will show you any errors thrown by the page and bean.
And then of course clearing all the data via the debug toolbar may put the system in an "unknown" state - and you should probably try to reload the page subsequently to ensure it is well-defined/-instantiated.
/John
I found this question, but it does not appear to be resolved, and I also have more to add.
First off, the linked question defines pretty much the same issue that I am having.
1. I am using the application layout control from the ExtLib
2. It does not matter if the button is in that control or not.
3. CSJS actions will fire from the button, SSJS actions will not fire.
4. No errors are present
5. Browser / cache is irrelevant as the server side action just will not fire.
After seeing the linked question, I looked in the Local file in the package view and found an anomaly that makes me wonder if it could be the cause. I have never seen such a file before and even looked in my other xpage projects just to be sure.
This file cannot be deleted, and when clicked upon, the display window says that the element does not exist.
Does anyone know what this file is, how I can remove it, or could it be that my application is corrupted?
**More Info **
The following snippet is copied from the java file for the XPage located in the Local directory. Everything looks fine to me.
private UIComponent createEventHandler(FacesContext context,
UIComponent parent, PageExpressionEvaluator evaluator) {
XspEventHandler result = new XspEventHandler();
String sourceId = "button2/xp:eventHandler[1]/xp:this.action[1]/text()";
MethodBinding action = evaluator.createMethodBinding(result,
"#{javascript:view.postScript(\"alert(\'server script fired!\')\");}",
null,null, sourceId);
result.setAction(action);
result.setSubmit(true);
result.setEvent("onclick");
result.setRefreshMode("complete");
return result;
}
EDIT
Moving all of the design elements into a new .nsf so that file is no longer present does not change the problem of the SSJS onclick action not firing. That strange file is however not present.
Is it failing on a converter / validator? That can cause it to skip out of the lifecycle before Invoke Application phase. To test whether a button is actually working, you can also use "Do not validate or update data". Then the SSJS runs in Apply Request Values phase. If the SSJS is triggered (you won't have the latest data from the browser in the data model or components though), then it's another good bet for converter or validator failure.
I am writing an app in which the end user can mark documents as his favourites. I already have the necessary forms and views to save and read back the favorites, but I have one little problem: all is done in the back end (the favorites are displayed from a view, not a scoped variable) and there seems to be a sync issue between the click on the "add to favorites" link and the partial refresh that reloads the favorites: the newly added document only shows after a second refresh. No problem then, I then decided to move the favorites in a managed bean I already have for the session where I keep the language and other user's prefs.
The problem I now face is this: how can I trap the login event so I can build the favorites list in the bean? I looked at how the "xInvolve" control's favorite piece was built and I found that there is some code added to the custom control's beforePageLoad event. But I'm wandering if there's a better way of doing this. If I go that way, this means that each time a page loads, it will go in my session bean to look if it needs to build the favorites list. I feel it's a bit of an overload: I'd rather build the list once, when the user logs in.
Is that possible or I should keep the beforePageLoad code just as it is now? Any ideas/thoughts are appreciated.
Note this does NOT actually trap the login.. I'm not sure how to do that exactly but I'm not sure it's nexessary..
Just thinking out loud here.. but I would try this..
Assume you have a "UserObject" to hold the favorites for the user.. then the object gets loaded it reads them in etc...
What about an "App Bean".. a managed bean in app scope... basically a factory for your application... inside the AppBean you have a Map... so that's an easy way to get a hold of the userobject anytime and it lives throughout the app scope...
Now on each page.. you try to grab the UserObject... if it doesn't exist.. you load it once into the App Bean so it's there for later... if it exists.. it's already loaded... you then update the UserObject when the user adds/removes a favorite... make sure to go through the object in the app bean to keep it updated...
An advantage of this is if the user opens another browser or tab.. it all pulls from the AppBean...
Give that some thought and let me know if you need any help...
Also note: This is a java solution... it might be workable in SSJS though..
A standard login page is HTTP only, not XPages. HTTP identifies that a login is required, validates credentials, checks the URL that you're redirecting to exists and only then does the XPages runtime take over.
If you want to trap login, you need to have an XPage making an AJAX request to do the login.
You might want to have a look at XPages Help Application on OpenNTF. I built a favouriting function into that. Favourites are stored in the person's profile, but for the duration of the session held in memory in a Map. The person's profile is created with the hashed value of the username. There is a potential issue with renames, but that's not a significant issue.
You could use a SessionListener. In the sessionCreated method you can calculate the List and initialize your session scoped bean:
public class MySessionListener implements SessionListener {
public void sessionCreated(ApplicationEx arg0, HttpSessionEvent event) {
FavBean bean = new FavBean();
// create the favorites List
// and push it to your bean
// add the bean to session scope
event.getSession().setAttribute("favBean", bean);
}
public void sessionDestroyed(ApplicationEx arg0, HttpSessionEvent event) {}
}
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??
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.
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?
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!
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.