have you ever wondered how values are treated as “multi-values” on the Notes backend document?
it’s pretty simple:
if you want to keep your design straightforward use as Form in the data binding section the same name as the xPage name:
this will allow you to open a document created with this xPage without knowing it’s corresponding xPage with the following URL command:
/path/to/your/db.nsf/$$OpenDominoDocument.xsp?documentId=UNIVERSALID
If you do it this way you do not even need to bind a notes form to the corresponding xPage:
Drawbacks?
there is… you cannot use the Data view to drag&drop the fields from the Domino form onto the xPage:
remember? it was first days, then hours, okay, today hours as well.. but I think with some help of you guys out there blogging also I found the final solution, and the times do get better and better, as I get more and more knowledge about the whole thing.
summary:
PhaseListener updates:
FacesContext facesContext = event.getFacesContext();
try {
// handle validators
if (event.getPhaseId().equals(PhaseId.PROCESS_VALIDATIONS)) {
// add validators if required
// i use:
//CustomValidator v = new CustomValidator();
//v.handleValidators();
// if not submitted via save button disable validators
// get eventHandlerClientId
String eventHandlerClientId = (String) facesContext
.getExternalContext().getRequestParameterMap().get(
“$$xspsubmitid”);
// extract eventHandlerId
String eventHandlerId = eventHandlerClientId.substring(
eventHandlerClientId.lastIndexOf(“:”) + 1,
eventHandlerClientId.length());
// get eventHandler “component”
UIEventHandler eventHandler = (UIEventHandler) Util
.findComponent(facesContext.getViewRoot(),
eventHandlerId);
// find the parameters set via Edit Event parameters on the
// Events page
boolean skipValidation = true;
List<Parameter> parameters = eventHandler.getParameters();
if (null != parameters) {
Iterator<Parameter> iterator = parameters.iterator();
while (iterator.hasNext()) {
Parameter par = iterator.next();
if (par.getName().equals(“requireValidation”)
&& par.getValue().equals(“true”)) {
skipValidation = false;
break;
}
}
}
if (skipValidation) {
toggleValidators(facesContext.getViewRoot(), true);
}
}
} catch (Exception e) {
// default disable validations for all other events which are not
// “requireValidation=true” and which might throw an error while trying to get their event handler
toggleValidators(facesContext.getViewRoot(), true);
}
if (event.getPhaseId().equals(PhaseId.PROCESS_VALIDATIONS)) {
FacesContext facesContext = event.getFacesContext();
toggleValidators(facesContext.getViewRoot(), false);
}
to speed up execution you should native Java code whereever possible!!!!!!!!! (should I add some more explanation marks???????) DID YOU GET THIS
okay.. fun aside… let’s get down to the hardcore stuff:
let’s assume you created a helper class doing some nice stuff for you on your XPage (such as logging to another place than is possible with DebugToolbar)
let’s assume your Logger is in the package com.yourcompanyname.xpages.utilities
now if you want to log the action of a button within an XPage you would need to add this code:
var Logger:com.yourcompanyname.xpages.utilities.Logger = new com.yourcompanyname.xpages.utilities.Logger()
Logger.log(“your message”)
what you might have forgotten though (actually you didn’t cause you just used it: JavaScript!)
so simply create a SSJS library which you include on every page you create.
add this line:
var Logger: com.yourcompanyname.xpages.utilities.Logger = new com.yourcompanyname.xpages.utilities.Logger();
you can then in your button simply log the message like this:
Logger.log(“your message”)
doesn’t that save you endless typing/correcting/testing etc?
think of the SSJS Library as a “proxy” to your native Java classes!
I am!
do they, who the hell are they anyway?, really expect you to add on 50 to 100 fields with all those clumsy interface buttons the normal validators?
examples? let’s count the steps it takes me to add a validator:
1. select the field in the XPage
2. open properties view
3. “enlarge” (or drag it upwards) the properties view (cause it’s always to slim in height)
4. click “All properties”
5. scroll down to data or click these on the first to sections:
6. click on the “validators” which makes some equally small buttons visible
7. click the plus button to add a validator:
8. select the validator you want to apply
9.-x. add the required property values
Now repeat that for about 50 or more fields on an XPage!!! I don’t think your customer is happy to pay your for this stupid/error prone clumsy work.
well I don’t know about you.. but I want to keep as much code centralized as I can. I am such a lazy person. So I tried to follow up on our method we had in our plain old Notes framework: field validation configured via setup documents!
So I had to figure out how custom validators work. This journey also took some hours, but within a day I got all working. And this included getting the JSF phases to understand (see my other post about that topic)
Here the result:
The display will be handled by the errorMessage/errorMessages control: I tend to think of the one which is connected to the field as the “summary” message (reason is it should no distract the look and feel of your input form too much), and the ones collecting all messages as the “detail” messages, but I think its actually the other way around
Code your validator:
public class CustomValidator implements Validator {
public void validate(FacesContext facesContext, UIComponent field,
Object arg) throws ValidatorException {
String value = arg.toString();
// add here your validation conditions for which a message should be
// displayed and then
// create a FacesMessage which then will show up next to your field in
// the
String m1 = “ups something went wrong: summary”;
String m2 = “ups something went wrong: detail”;
FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, m1, m2);
throw new ValidatorException(msg);
}
}
DO NOT HANDLE THE EXCEPTION!!!
first I had a try/catch block installed.. but what happened? the messages showed up and yet the document could be save anyway!!! what a disaster… so get rid of the try/catch and let the ValidatorException be thrown and handled by the servlet engine.
faces-config.xml:
<validator>
<validator-id>idOfYourOwnValidator</validator-id>
<validator-class>com.abb.xpages.framework.CustomValidator</validator-class>
</validator>
we are done! wait!!! and how do you get this validator applied? wasn’t that the whole point? not having to repeat all those 10 steps over and over again?
well… please read this post about the phase-listener:
http://in-mood.blogspot.ch/2012/09/jsf-phases-in-xpages.html
there you add a condition which will in the beforeValidations phase add all the required validators:
add this to your phase-listener:
if (event.getPhaseId().equals(PhaseId.PROCESS_VALIDATIONS)) { CustomValidator v = new CustomValidator(); v.addValidators(); }
add this to your CustomValidator:
public void addValidators(){
// repeat this step (maybe in a proper loop for all fields which require the validator
// Util is a class I created with an abstract method “findComponent”, abstract such that I don’t
// have to always instantiate it whereever I use it
field = (UIInput) Util.findComponent(FacesContext
.getCurrentInstance().getViewRoot(),
“fieldNameOnTheXPage”);
// add your validator
Validator[] validators = field.getValidators();
// make sure you only add the validator once, otherwise the
// messages control will display one
// message for every roundtrip the page has done to the server!
if (0 == validators.length) {
field.addValidator(facesContext.getApplication()
.createValidator(“idOfYourOwnValidator“));
}
for single instances you can of course add it manually as well (such as for testing if it actually works:)
now there is one minor drawback in this whole process:
on my local machine the “createValidator” won’t work due to security issues.
on our test server it works, I assume it has to do with a grant * something statement I once added to the java.policy in the jvmlibsecurity directory of the program directory
update:
Add this to the grant section:
permission java.lang.RuntimePermission “getClassLoader”;
I am actually now “finetuning” the “addValidators” as I could not yet figure out how to create the “required” Validator myself such that it adds the message to the control instead of alerting it in the client!
I was trying to setup a system to handle “validation” via setup documents
the normal validators which can be set up directly on each field would not help much (or be too much work to implement)
I first tried a simple method: isDocValid returning true/false and adding JSF Messages like following:
String m1 = “summary: ups something went wrong”;
String m2 = “detail: ups something went wrong”;
msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, m1, m2);
facesContext.addMessage(field.getClientId(facesContext), msg);
I would call this methid in the querySaveEvent of the XPage, and it seemed (at least for new documents) to work well.. until I opened a document from a view, and oh wonder (maybe it’s a feature and not a bug) the querySaveDocument event was not even triggered…
That was bad news: I also had a “save as draft” button which would let the user return to the document, but if the validation then would fail?
nope.. I started looking for another way…
so I talked to a guy who has a LOT, I mean a LOOOOOOOOOOOOOT, of JSF experience.
With his help and some from my best friend (google) I did the following:
created a phase listener which logged all events (great for debugging, see those “immediate” events)
a very good article about the JSF phases can be found here:
http://www.ibm.com/developerworks/library/j-jsf3/
a MUST read for any serious XPages developer with little or no JSF experience!!
so then thanks to the phase listener I could see the how all phases process.
and now I immediately had also a way to “plugin” my custom validators properly (more on that in another post)
so here what I did for the phase listener:
Java class:
public class PhaseListener implements javax.faces.event.PhaseListener {
public void beforePhase(PhaseEvent event) {
// log your beforePhase with event.getPhaseId()
}
public void afterPhase(PhaseEvent event) {
// log your afterPhase with event.getPhaseId()
}
}
faces-config.xml:
<lifecycle>
<phase-listener>package.of.your.PhaseListener</phase-listener>
</lifecycle>
now you can add simple additional code in any phase (before or after)
just use something like:
if (event.getPhaseId().equals(PhaseId.PROCESS_VALIDATIONS)) {
// run your code
}
PhaseId. (dot control space) will list all the CONSTANTS to check for any of the phases
since I have no started digging deeper into the XPages mysteries I want to share some of my learnings.
As most of you know: if you use Java in Lotus Notes/Domino you have to take care that you recycle the Notes objects you instantiated (session, database, document etc.)
Java has a nice feature called “finally”. If you use a try/catch block (which I do to log all errors happening, so that I can follow up and fix them) you can also add the “finally” block at the end..
It could serve pretty well for incinerating (I bought that term form some other blogger) the Notes objects…
But beware: I created a class which should be called upon the querySaveDocument event. As soon as I added the finally block the page would crash, most of the times so bad that my whole Notes/Designer went down too…
After hours (again hours!!! can you imagine what my week looks like???) putting log code moving it down one by one… until I reached the try block, nothing worked anymore…
but luck I figured: why not remove the finally block.. and miraculously the XPage could be displayed again
I don’t know what/why exactly it crashed and if you always should not use the finally block, try it.. if your system crashes, remove it and handle it in another way
again RTFM!!!! but hey… the FM not always tells you what you want to know!
Might that be the reason why we developers tend to NRTFM?
dunno..
anyhow… after DAYS of testing / searching (is it called googling nowadays?) / changing code and iterating a (felt) thousand times…
I finally came accross the solution how to “persist” the requestScope from one xPage to another…
First (I hope I am not wrong with this) a bit of background:
The parameters set in the requestScope would (normally) get submitted via the URL (in a GET request). As those JSF (and xPages) mostly work via POST request we (unless we use nice tools, such as the TCP Monitor in the designer!) never really see them. That’s one of the reasons so many Domino developers struggle to get the requestScope working. We cannot really debug it properly.
Thanks to TCP Monitor and a friend helping me, we finally figured:
After an xPage has been submitted and the navigation rule would redirect it to another page this redirection was a request to the client browser!
Imagine: The server tells the browser: please ask now for page xyz.xsp
And with the little background above you can understand that the server didn’t tell the browser which requestParameters it should transmit as well, alas they would be (ARE!!!) lost.
But as i said.. I did a lot of research on this and I found the following setting: (You find it under the “Package explorer” view in the designer
if you turn this feature off the server does not ask the browser for asking the new page but the server actually redirects himself to the new page and hence will preserve the requestScope values….
So you can actually have the dimmwitted example of two xPages where on one page you enter the name and submit it, and the second page will greet you with “Hello yourname” works!
I for myself will use this feature to tell my xpView.xsp (the one which handles all views in my db) will know which view it has to display after a document has been submitted (at least if nothing else has been defined)
It can be done! And it’s actually not even that complicated.
Find the instructions here:
http://www-10.lotus.com/ldd/ddwiki.nsf/dx/16102009115724SCAEXA.htm
or read on…
there is this nice Design Definition property on all custom controls (starting 8.5.3 I think):
if you add some xPage source code to it (tip: create a new xpage the way you would like to look this custom control in the design view and copy & paste its source code here)
it will later on in the actual xPage look like this:
in this example i used is as a way to “document” some settings required to make it work properly.
But of course you could, if you have, for a company framework, also paste in a picture of a standard menu, the footer or the header of your pages.
then while designing the xPage you get a better feeling on how the page will look like later.
and it gets even better.. just read an article, that this design definition is “dynamic”, means you can actually have the custom control look different, ie. depending on custom properties you set.
Read this article: http://dontpanic82.blogspot.ch/2010/01/xpages-using-powerful-design-definition.html
okay okay.. I know.. RTFM
but hey.. we are developers, we know what we are doing, right? 😉
How it all began:
I was first trying to install the Extension Library on my desktop manually. and of course I installed the Domino part instead of the Designer part 🙁
I then considered following those instructions here:
http://www-10.lotus.com/ldd/ddwiki.nsf/dx/Installing_the_upgrade_pack_on_Domino_Designer_and_Notes_ddxl853
but again RTFM…
I mean c’mon… can’t be that complicated.
It can!!! After 2 days of trying (installing/uninstalling both LN/FP/UpgradePack) to get this upgrade pack installed I almost gave up.
But then I took a quiet moment and told me: one last time.
Interestingly the modules had been installed in previous attemps, only I could not load any xPage with modules from the extension library as something didn’t install 100% correctly! I always got the Error 500 command not handled exception.
With those features installed… I could finally figure out the exact filenames to check and remove manually any directory/file which was still in the mentioned (see link above) directories. If Windows 7 search would have done a better job I wouldn’t have needed those exact filenames though, maybe some of my anger goes to MS.
so here is how I found the exact names:
directories searched: