Mindoo Blog - Cutting edge technologies - About Java, Lotus Notes and iPhone

  • 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?
    Here is the first helper class that has an answer to all three questions:

    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

    1Veer  20.07.2009 20:31:32  XPages series #5: Helper classes to access the JSF environment and Lotus Notes data

    Wow!! I love this stuff. I am going to try this out today.!!!!

    2Markus Köstner  21.07.2009 14:38:06  XPages series #5: Helper classes to access the JSF environment and Lotus Notes data

    Hi.

    Thank you for sharing that. I justed started with xpages and now I see the real advantage. We already use eclipse plugins for a better user experience. And with this, we can reuse the already developed models for xpages.

    Great Job!

    3Edwin  10.08.2009 9:56:34  XPages series #5: Helper classes to access the JSF environment and Lotus Notes data

    Hi,

    Thanks for sharing the useful information.

    { Link }

    4Laura  30.11.2009 17:39:01  XPages series #5: Helper classes to access the JSF environment and Lotus Notes data

    Thanks, I can see where this is really going to help me! However, I need a little assistance with the UIComponent findComponent class.

    I am using 8.5.1 gold... and have used the same imports you did (copied your code verbatum to start), but getting numerous errors. Did you code in 8.5.1 or 8.5? Could there be differences between the two? Or do I just need more imports?

    First error....

    "UIComponent cannot be resolved to a type"

    .... can be fixed by importing javax.faces.component.UIComponent

    Second error....

    "List cannot be resolved to a type"

    (on the "List childComponents".... line)

    .... assumed I needed to import either java.util.List or java.awt.List or some other import. But, if I import any of those, then the topComponent.getChildren() says: "Type mismatch: cannot convert from List to List"

    I could fix this last error by casting to List... but then the childComponents for loop has a new problem:

    "Can only iterate over an array or an instance of java.lang.Iterable"

    I must be missing something.....

    Any help would be greatly appreciated!!

    Thanks.

    5Karsten Lehmann  30.11.2009 17:48:02  XPages series #5: Helper classes to access the JSF environment and Lotus Notes data

    Hi Laura!

    The blog database treated some of my code as html, so something is missing in the browser output.

    Regarding your questions: the List is a java.util.List.

    And the line should be

    List < UIComponent > childComponents=topComponent.getChildren();

    It's a generic Java List of UIComponent objects, I hope this time the code goes through.

    If not, please take a look at the HTML source code if this page. There you should find the full syntax.

    6Laura  30.11.2009 19:56:56  XPages series #5: Helper classes to access the JSF environment and Lotus Notes data

    EXACTLY what I needed!! Thanks sooo much.

    7Johnny Jiang  30.06.2010 16:05:32  XPages series #5: Helper classes to access the JSF environment and Lotus Notes data

    Hi Karsten,

    Your blog has been really helpful for me, thanks a lot.

    Johnny

    8A.G.Suriyan  11.08.2011 20:55:49  XPages series #5: Helper classes to access the JSF environment and Lotus Notes data

    Hi Karsten,

    Please tell me why you are creating some function for getting current session and database handle, you can able to get the database and session handle through

    Database currentDB = (Database)FacesContext.getCurrentInstance().getApplication().getVariableResolver().resolveVariable(FacesContext.getCurrentInstance(),"database");

    Session currentSession = (Session)FacesContext.getCurrentInstance().getApplication().getVariableResolver().resolveVariable(FacesContext.getCurrentInstance(),"session");

    I understood your function also do the same thing.. If there is any special reason for doing like that?

    9Gobinath S  28.07.2013 20:00:11  XPages series #5: Helper classes to access the JSF environment and Lotus Notes data

    Thanks for the great information Karsten!

    For getting Session and Database, You may also use

    import com.ibm.xsp.model.domino.DominoUtils;

    Session ss = DominoUtils.getCurrentSession();

    Database db = ss.getCurrentDatabase();