XPages series #5: Helper classes to access the JSF environment and Lotus Notes data
Karsten Lehmann 18 July 2009 19:17:37
In this weekend posting, I would like to introduce two helper classes that you will probably need for your first steps.After I had found out how to declare and bind Managed Java Beans in XPages, I tried to produce a small sample application.
Soon, three questions came up, that I needed to solve, before I could go on:
- How do I access global JavaScript variables and my declared Java beans?
- How can I access fields in the current XPage?
- Calling NotesFactory.createSession() to create a Notes session throws a security exception.
Is there a way to access Notes API from my bean code?
package com.acme.tools;
import javax.faces.application.Application;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
public class JSFUtil {
/**
* The method creates a {@link javax.faces.el.ValueBinding} from the
* specified value binding expression and returns its current value.<br>
* <br>
* If the expression references a managed bean or its properties and the bean has not
* been created yet, it gets created by the JSF runtime.
*
* @param ref value binding expression, e.g. #{Bean1.property}
* @return value of ValueBinding
* throws javax.faces.el.ReferenceSyntaxException if the specified <code>ref</code> has invalid syntax
*/
public static Object getBindingValue(String ref) {
FacesContext context=FacesContext.getCurrentInstance();
Application application=context.getApplication();
return application.createValueBinding(ref).getValue(context);
}
/**
* The method creates a {@link javax.faces.el.ValueBinding} from the
* specified value binding expression and sets a new value for it.<br>
* <br>
* If the expression references a managed bean and the bean has not
* been created yet, it gets created by the JSF runtime.
*
* @param ref value binding expression, e.g. #{Bean1.property}
* @param newObject new value for the ValueBinding
* throws javax.faces.el.ReferenceSyntaxException if the specified <code>ref</code> has invalid syntax
*/
public static void setBindingValue(String ref, Object newObject) {
FacesContext context=FacesContext.getCurrentInstance();
Application application=context.getApplication();
ValueBinding binding=application.createValueBinding(ref);
binding.setValue(context, newObject);
}
/**
* The method returns the value of a global JavaScript variable.
*
* @param varName variable name
* @return value
* @throws javax.faces.el.EvaluationException if an exception is thrown while resolving the variable name
*/
public static Object getVariableValue(String varName) {
FacesContext context = FacesContext.getCurrentInstance();
return context.getApplication().getVariableResolver().resolveVariable(context, varName);
}
/**
* Finds an UIComponent by its component identifier in the current
* component tree.
*
* @param compId the component identifier to search for
* @return found UIComponent or null
*
* @throws NullPointerException if <code>compId</code> is null
*/
public static UIComponent findComponent(String compId) {
return findComponent(FacesContext.getCurrentInstance().getViewRoot(), compId);
}
/**
* Finds an UIComponent by its component identifier in the component tree
* below the specified <code>topComponent</code> top component.
*
* @param topComponent first component to be checked
* @param compId the component identifier to search for
* @return found UIComponent or null
*
* @throws NullPointerException if <code>compId</code> is null
*/
public static UIComponent findComponent(UIComponent topComponent, String compId) {
if (compId==null)
throw new NullPointerException("Component identifier cannot be null");
if (compId.equals(topComponent.getId()))
return topComponent;
if (topComponent.getChildCount()>0) {
List childComponents=topComponent.getChildren();
for (UIComponent currChildComponent : childComponents) {
UIComponent foundComponent=findComponent(currChildComponent, compId);
if (foundComponent!=null)
return foundComponent;
}
}
return null;
}
}
How to access other beans and bean properties
You can use the methods JSFUtil.getBindingValue() and JSFUtil.setBindingValue() to do that:
//Returns our previously declared session scope bean ActorList. The bean is created if it does not exist yet
ActorList actors=(ActorList) JSFUtil.getBindingValue("#{ActorList}");
//Get the value of a bean property
List<Actor> actorList=(List<Actor>) JSFUtil.getBindingValue("#{ActorList.actors}");
//Set the value of a bean property
JSFUtil.setBindingValue("#{ActorList.actors}", new ArrayList<Actor>());
Hint:
The expression language is more powerful than you might think. An expression like
#{ActorList.actorsById["12345"].firstname}
could for example return the firstname of the actor with the unique identifier "12345" from the ActorList bean.
What the expression resolver does internally is grab your declared ActorList bean from the session scope, look out for a method "getActorsById()" and check its return value. If the return value is of type java.util.Map (like a List in Lotusscript), if would then call its get method with the hash key "12345" (like Map.get("12345")) to get the Map entry for that key. In our case, this could be an Actor object.
Finally, the resolver then searches for a method getFirstname() in the object and returns its value.
How to access fields in the current XPage
An XPage (like any JSF page) consists of a tree of UIComponent objects. The top element of that tree can be retrieved with
UIViewRoot view=FacesContext.getCurrentInstance().getViewRoot();
Our helper function JSFUtil.findComponent(String) traverses the whole component tree and searches for an UIComponent with the specified identifier.
That's basically what the XPage JavaScript function getComponent(String) does.
So the following code
UIComponent comp=JSFUtil.findComponent("actorTable");
would for example give us access to the table of actors that we created in the previous article of this series.
It depends on the component type, how much we can do with the components. UIComponent is just the base class that all the various UI components extend. We will see an example use case for accessing the component tree in a later article.
How to access global JavaScript variables / How to get the current Lotus Notes Session
It you know the answer to the first question, you know the answer to the second. That's because the current Lotus Notes session and database are stored in global JavaScript variables.
So here is helper class number two, that leverages the new JSFUtil.getVariableValue(String) function to grab a global JavaScript variable:
package com.acme.model.notes;
import lotus.domino.Database;
import lotus.domino.Session;
import com.acme.tools.JSFUtil;
public class DominoAccess {
/**
* Returns the current Notes session instance of the Javascript engine.
*
* @return Session
*/
public static Session getCurrentSession() {
return (Session) JSFUtil.getVariableValue("session");
}
/**
* Returns the current Notes database instance of the Javascript engine.
*
* @return Database
*/
public static Database getCurrentDatabase() {
return (Database) JSFUtil.getVariableValue("database");
}
}
Sample usage:
Session session=DominoAccess.getCurrentSession();
String userName=session.getUserName();
This returns the user name of your Lotus Notes ID if you are logged in and the name of the Domino server ID if you are not logged in.
Hint
Please note that the session and all child Notes objects like databases or documents are disposed (by calling their recycle() method) quite often. I haven't figured out so far when this exactly happens.
So do not store Notes objects locally in your code. Instead, grab a fresh session, transfer the Notes data into your own objects and do not forget to call recycle() on Notes objects that you don't need any more (like for normal Notes Java API coding) to avoid high memory allocation.
That's it for today. A very technical article without a screenshot or real-life example. Sorry about that. But it's necessary to have this knowledge base for the next articles.
I'm curious if anybody has already started to build his own sample application. Feel free to write a comment!
- Comments [9]