Last week I spoke with Tony McGuckin at IBM Connect. This blog post is explaining a problem I had recently, the cause, the investigation and the resolution two possible resolutions. All relate to content I covered in that session, particularly around the JSF lifecycle. It’s also why the JSF lifecycle is something I always cover at the very start of the Troubleshooting XPages element of the XPages training course I’ve developed and delivered for Intec.

The Problem

The problem was I had a repeat control bound to a requestScope variable, with buttons in the repeat to perform an action on the current row. That sounds straightforward, but the SSJS on the buttons (it was calling a Java method, so only one line of code) was not running. With the Java debugger switched on and a breakpoint in my Java method, the breakpoint was not being triggered. Even with a simple print("Running...") I was getting no response.

The Investigation

All my applications these days have a Phase Listener in them. So the first step was to add a breakpoint in the Phase Listener and check what phases it was running through. My apps also have a sessionScope variable I can set to print out the phases to the console.

As I covered in my session, SSJS normally runs in INVOKE_APPLICATION phase. Debugging showed it was running through all six phases. So the code should be triggered, but it wasn’t. Trying various things , I finally got the code to trigger if I set repeatControls="true" on the repeat. Not an ideal solution, and not the one I went for, once I understood the cause.

The Cause

During a partial refresh, the component tree is retrieved. The lifecycle then runs through a number of phases, during which various code is recalculated.

So there were two aspects at play here.

  1. A Repeat Control, by default, is stored in the component tree as a single set of data-agnostic controls. That means one set of controls are stored, with no binding to specific data items. Each time the component tree is loaded or updated, the data bindings are re-evaluated based upon the value, the start number, the number of rows etc. stored in the Repeat Control’s properties. With repeatControls="true", however, the component tree stores x sets of the controls, each bound to a specific value, calculated on page load only based upon the value, the start number, the number of rows etc that were in the Repeat Control’s properties at load time.
  2. Various code is recalculated and the component tree updated.

What this means is that with repeatControls="true", the value property and the bindings were not re-evaluated. With them, my binding of value="#{requestScope.newLinks}" was getting re-evaluated and returning null because requestScope had now been cleared. There were no contents for the repeat, so by the time it got to the INVOKE_APPLICATION phase there was no button in the component tree with an eventHandler to run. So there was no SSJS being triggered.

The Resolutions

While writing this blog post I realised the simplest resolution. What is possibly not widely realised is that Expression Language – e.g. #{requestScope.newLinks} – can also be set as computed on page load. So ${requestScope.newLinks}. Unlike setting repeatControls="true", a pager would still work, if required, because the value was fixed but the elements being iterated over for each row could still be changed. Of course then the only way to update the contents of the repeat is to do a full page refresh.

The alternate answer is to use viewScope. So I changed that, both in the XPage and the Java code loading the value. And got an error 500 page. Checking the logs showed a NullPointerException on the line setting the viewScope variable. Debugging confirmed that viewScope was null when I was trying to assign the variable, because of where the code was running.

In standard SSJS, you’d add it to the beforePageLoad of every XPage it’s needed on or a Custom Control. But adding to a Custom Control means fragmented code making it harder to support later on. Which is why I’ve moved, for some time now, to using Jesse Gallagher’s controller framework in XPages Scaffolding. So the code was running in the ControllingViewHandler class, which is a ViewHandler and handles loading the component tree before beforePageLoad even runs. But I was trying to set viewScope before the component tree had been loaded, and it is that core method to load the component tree which initialises viewScope.

But you don’t need to use #{viewScope.newLinks} to retrieve that viewScope variable. You can just use #{newLinks}. #{newLinks} calls the VariableResolver, which looks through all variables like database or session (usually called implicit variables). if you’ve looked carefully at Mark Leusink’s debug toolbar or have looked at the code in OpenNTF Domino API, you’ll see those variables are stored in requestScope. As someone told me a while ago (sorry, I can’t remember who, possibly Tim Tripcony or Nathan T Freeman), the VariableResolver looks through all scopes, from requestScope upwards. So you don’t explicitlyneed to include requestScope, viewScope etc. Obviously it’s best practice, because you can have the same key in multiple scopes (viewScope.newLinks and sessionScope.newLinks), so adding the scope as well ensures you get the entry you want. And someone else might end up adding a variable name that clashes.

But there are also more discreet scopes, like in a Repeat Control. If you trigger a call to the variableResolver with Expression Language in a repeat control, it will also look to implicit variables defined for the Repeat Control, then through all defined for any Panel it’s in (dataContexts, dataObjects, datasources etc), then through all defined for Custom Control it’s in, up through any more Custom Controls to the XPage. I would expect it to start at the tightest scope and work outwards, so only after getting to the XPage checking requestScope, viewScope, sessionScope and applicationScope. (I expect that SSJS variables and objects are also traversed, another reason to minimise use of SSJS.) So that’s another reason for trying to using requestScope, viewScope etc where possible.

The solution I went for (incidentally the solution Jesse uses for his pageController variable, that binds to the relevant Java class for the XPage) was to load it to requestScope initially. Then after the component tree had been loaded, move it to viewScope. Then in my XPage call it via just the variable name. Of course I modified the variable name to minimise, as much as possible, the risk of conflict.

Conclusions

  1. Understanding the life cycle is very important to quickly diagnose “obvious” mistakes.
  2. Understanding what’s happening under the hood helps manage expectations of behaviour.
  3. Understanding what’s happening under the hood helps you manipulate things to get the behaviour you want!

The slides from my session with Tony McGuckin are on SlideShare.

1 thought on “Scoped Variables, Implicit Variables and Repeat Controls”

  1. Pingback: DataSources and ViewScope

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.