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

  • XPages series #6: Tracking data changes

    Karsten Lehmann  July 22 2009 17:52:10
    We will change and add a lot of code in our XPages/JSF sample implementation in this blog posting, because our current Movie Actor application does note handle data changes very well.

    Remember part 4 of this series, when we introduced the Actor and ActorList implementation:
    Our ActorList Java bean is the so called backing bean behind the ActorList XPage, which means that it is used to provide the data and application logic needed by that XPage.
    Moving all the code from the XPage to the backing bean helps us to separate the UI from the business logic. It improves code reuse and testing/debugging.

    In part 4 I described how to show your own Java objects in a data table control and in a repeat control. We used this technology to display a list of Actor objects in read-only mode.
    As I said in the article, you can replace the computed fields in the data table by edit boxes to make the Actor object list editable. JSF then calls the setter methods of the Actor objects with the new values.

    That works, but the architecture isn't really smart that way.

    In this article, I would like to add a data store to our little scenario, that we will use to store the data changes to a persistent data storage.
    That way we can
    • check whether the current user is allowed to change the data
    • write the changes and the previous values to a log file (e.g. for auditing purpose)
    • do data transformation, like storing the information of one data object in two places (e.g. two Notes documents or Notes and SQL)
    • trigger additional data changes, like inheriting field values to/from other documents

    In short: We get complete control over the data that is written to disk.

    Our today's result will look like this:

    Image:XPages series #6: Tracking data changes

    (we concentrate on the functionality first, this is not a web design series ;-) )


    It's the data table of part 4, but with edit boxes and a button "Save changes". That button triggers the save operation to store the modified Actor table row entries into our own data store.
    The logging field at the bottom contains a list of data comparison results (I had changed the comment property of the first Actor in the list and pressed the save button).

    Our data store

    Let's start with a simple Java interface definition of our data store:

    package com.acme.actors.model;
    import java.util.List;

    /**
     * Interface for a CRUD-based Data Store
     */

    public interface DataStore {
           /**
             * The method creates a new Actor instance
             *
             * @return new Actor
             */

            public Actor createActor();

           /**
             * The method searches for an Actor by its ID in the data store
             *
             * @param id Actor ID
             * @return Actor or null
             */

            public Actor findActorById(String id);

           /**
             * Writes changes made to the specified Actor instance to the data store
             * to store them permanently.
             *
             * @param changedActor Changed Actor instance
             */

            public void updateActor(Actor changedActor);

           /**
             * The method delete an Actor from the data store
             *
             * @param id Actor ID
             */

            public void deleteActor(String id);

           /**
             * The method returns a list of Actor objects, ordered by the lastname property.
             * This list cannot be modified. Use the DataStore methods instead.
             *
             * @return List
             */

            public List<Actor> getActorsByLastname();
    }


    That's a data store interface for our Actor objects following the CRUD pattern. CRUD stands for Create, Read, Update and Delete.
    It's not a concrete implementation. A Java interface acts like a blueprint for the implementations: they have to implement all the methods of the interface.

    Our implementation, using an in-memory list of objects can be found here: The first interesting thing is how we tell JSF about this concrete data store implementation. We use a new managed bean for that, called DataStoreManager:

    package com.acme.actors.model;

    import javax.faces.FacesException;
    import com.acme.tools.JSFUtil;

    /**
     * The DataStoreManager manages the {@link DataStore} instance that is
     * used for the data storage of the application.
     */

    public class DataStoreManager {
            private String m_storageClass;
            private DataStore m_store;

            public DataStoreManager() {}

           /**
             * This method is used by JSF to set the class name of the
             * {@link DataStore} to be used.
             *
             * @param className class name
             */

            @SuppressWarnings("unchecked")
            public void setStorageClass(String className) {
                    m_storageClass=className;
                    try {
                            //try to initialize the data store using Java reflection
                            Class<? extends DataStore> storageClazz=(Class<? extends DataStore>) getClass().forName(className);
                            m_store=(DataStore) storageClazz.newInstance();
                    } catch (ClassNotFoundException e) {
                            throw new FacesException(e);
                    } catch (IllegalAccessException e) {
                            throw new FacesException(e);
                    } catch (InstantiationException e) {
                            throw new FacesException(e);
                    }
            }

           /**
             * Returns the class name of the {@link DataStore} to be used.
             *
             * @return class name
             */

            public String getStorageClass() {
                    return m_storageClass;
            }

           /**
             * Returns the data store instance for the data access.
             *
             * @return data store
             */

            public DataStore getStore() {
                    return m_store;
            }

           /**
             * Convenience method to access the instance of the DataStoreManager for
             * the current user from the session scope.
             *
             * @return DataStoreManager instance
             */

            public static DataStoreManager getManager() {
                    return (DataStoreManager) JSFUtil.getBindingValue("#{DataStoreManager}");
            }
    }


    Take a look at two methods in particular: setStorageClass(String) and the static method getManager().

    We use setStorageClass(String) to tell the DataStoreManager bean which implementation of the DataStore interface it should use. It then tries to instantiate the concrete data store, for our test implementation the class is "com.acme.actors.model.mydb.MyDataStore".

    The static method getManager() uses one of the helper functions of series part 5 to return the session scope instance of the DataStoreManager.

    So in our Java code, we can quickly do data store operations like this

    DataStore ds=DataStoreManager.getManager().getStore();
    ds.deleteActor("12345");

    Actor newActor=ds.createActor();
    newActor.setFirstname("Humphrey");
    newActor.setLastname("Bogart");
    newActor setComment("Casablanca");
    ds.updateActor(newActor);


    JSF will take care that the DataStoreManager bean is created when we call getManager(). We can even call getManager() before the bean is used anywhere in an XPage.

    Please note that the code above does not contain any Lotus Notes specific code like bindings to Notes documents and views, just our own simple Java classes and interfaces.
    Data handling is done in our code.

    Setting default bean properties

    How do we know when to tell the DataStoreManager the class name of our data store?
    That's easy: We let JSF do it automatically. By adding a small piece of code in the faces-config.xml, we can define default values that should be set when a bean is created by JSF.

    <?xml version="1.0" encoding="UTF-8"?>
    <faces-config>
      <managed-bean>
        <managed-bean-name>DataStoreManager</managed-bean-name>
        <managed-bean-class>com.acme.actors.model.DataStoreManager</managed-bean-class>
        <managed-bean-scope>session</managed-bean-scope>
       <managed-property>
          <property-name>storageClass</property-name>
          <value>com.acme.actors.model.mydb.MyDataStore</value>
        </managed-property>

      </managed-bean>

      <managed-bean>
        <managed-bean-name>ActorList</managed-bean-name>
        <managed-bean-class>com.acme.actors.controller.ActorList</managed-bean-class>
        <managed-bean-scope>session</managed-bean-scope>
      </managed-bean>

      <managed-bean>
        <managed-bean-name>Log</managed-bean-name>
        <managed-bean-class>com.acme.logging.Log</managed-bean-class>
        <managed-bean-scope>session</managed-bean-scope>
      </managed-bean>
    </faces-config>


    You can set more than just string properties. For example, you can fill a Map with key/value pairs or predefine the values of a Java List. Please refer to the JSF documentation I mentioned in part 2 of this series for the exact syntax.

    Detecting object property changes

    There are certainly many ways to track property changes for the data objects. For this example, I chose to store a list of ObjectPropertyChangeEvents in my data objects. Such a change event basically is a container for the name of the property (like "Firstname", "Lastname", "Comment"), its old and its new value. We will generate such events in the setter methods of our new Actor class implementation like this:

    public void setFirstname(String newFirstName) {
            if (!StringUtil.isEqual(m_firstName, newFirstName)) {
                    //first name has changed. Store old and new value in a change event
                    ObjectPropertyChangeEvent evt=new ObjectPropertyChangeEvent(FLD_ACTOR_FIRSTNAME, m_firstName, newFirstName);
                    m_firstName=newFirstName;
                    addChangeEvent(evt);

            }
    }


    Here is the base implementation of a data object. It contains all the property change management methods. Our Actor class will now be a subclass of DataObject  to leverage those methods.

    package com.acme.actors.model;

    import java.util.Hashtable;

    /**
     * Abstract base object for a data store object that provides an internal list
     * of changed object properties. The purpose is to quickly get information
     * in a data source what property changes have to be transferred to
     * persistent storage.
     */

    public abstract class DataObject {
            private boolean m_initialized=false;
            private Hashtable<String, ObjectPropertyChangeEvent> m_changeEvents;

            public DataObject() {}

            protected void init() {
                    if (!m_initialized) {
                           //lazy creation of member variables
                            m_changeEvents=new Hashtable<String, ObjectPropertyChangeEvent>();
                            m_initialized=true;
                    }
            }

           /**
             * Method to check whether properties in this object have been changed.
             *
             * @return <code>true</code> if there are changed properties
             */

            public boolean hasChanges() {
                    init();

                    return !m_changeEvents.isEmpty();
            }

           /**
             * Returns the property change events for this object
             *
             * @return change events or an empty list
             */

            public ObjectPropertyChangeEvent[] getChanges() {
                    init();

                    return m_changeEvents.values().toArray(new ObjectPropertyChangeEvent[m_changeEvents.size()]);
            }

           /**
             * Adds a change event to the internal list. If the property has been changed
             * before, the old and new change event will be merged into one.
             *
             * @param evt change event
             */

            protected void addChangeEvent(ObjectPropertyChangeEvent evt) {
                    init();

                    String fieldId=evt.getFieldId();
                    ObjectPropertyChangeEvent oldEvt=m_changeEvents.get(fieldId);
                    if (oldEvt==null) {
                            m_changeEvents.put(fieldId, evt);
                    }
                    else {
                           //merge old and new event
                            Object oldValue=oldEvt.getOldValue();
                            m_changeEvents.put(fieldId, new ObjectPropertyChangeEvent(fieldId, oldValue, evt.getNewValue()));
                    }
            }

           /**
             * Method to clear the change event list
             */

            public void resetChanges() {
                    m_initialized=false;
                    init();
            }
    }


    Here is the new Actor implementation and the source code of the ObjectPropertyChangeEvent:
    Actor.java - com.acme.actors.model.Actor
    ObjectPropertyChangeEvent.java - com.acme.actors.model.ObjectPropertyChangeEvent

    Our data store is now ready to use and our Actor data objects are prepared to write a change event log when somebody calls the setter methods.

    Adding the "save changes" operation

    At first, we now have to produce code in our ActorList backing bean that handles the save operation: It grabs the changed Actor objects from the data table and sends them to the persistent data store.
    We use another of our helper function for this purpose: JSFUtil.findComponent(String). It traverses the JSF component tree of the current XPage to give us access to the data table (like the getComponent() method does in XPages JavaScript).

    This is our new ActorList backing bean:

    package com.acme.actors.controller;

    import java.util.List;
    import javax.faces.component.UIComponent;
    import javax.faces.component.UIData;
    import com.acme.actors.model.Actor;
    import com.acme.actors.model.DataStoreManager;
    import com.acme.tools.JSFUtil;

    public class ActorList {
            public ActorList() {}

            public List<Actor> getActorsByLastname() {
                   //delegate call to the new data store
                    return DataStoreManager.getManager().getStore().getActorsByLastname();
            }

           public void save() {
                   //grab the data table content and send it to the data store
                    UIComponent table=JSFUtil.findComponent("actorTable");
                    if (table instanceof UIData) {
                           //the table is a subclass of UIData, which gives us access to the row values of the table
                            UIData tableData=(UIData) table;

                            int rows=tableData.getRowCount();
                            int oldRowIndex=tableData.getRowIndex();

                            for (int i=0; i                                tableData.setRowIndex(i);
                                    Object currObj=tableData.getRowData();
                                    if (currObj instanceof Actor) {
                                            Actor currActor=(Actor)currObj;
                                           //send the Actor object to the data store to write changes
                                            //to the persistent storage

                                            DataStoreManager.getManager().getStore().updateActor(currActor);
                                    }
                            }
                            tableData.setRowIndex(oldRowIndex);
                    }
                    else {
                            Log.getInstance().println("Could not find table with id=actorTable");
                    }
            }

    }


    Finally, we need to add the "Save changes" button to the XPage UI with the following JavaScript onClick code:

    var actorList=com.acme.tools.JSFUtil.getBindingValue("#{ActorList}");
    actorList.save();


    Here is a list of remaining files that have been used or changed in this article:
    Ok, guys... the worst part of this series is over ;-). It has become a really long article, even longer than part 4.

    But it's easier than it looks like. Believe me. :o)


    Comments

    1Adrian Stefanescu  09/03/2009 14:25:35  XPages series #6: Tracking data changes

    Hallo,

    vielen Dank für diese Serie von Tutorials (nach meiner Meinung eine der interessantesten Publikationen zum Thema Xpages).

    Endlich wurden einige Fragen, die sich mir bei der Betrachtung der Xpages gestellt haben, beantwortet. Wie kann man eine Trennung zwischen Präsentation und Logik realisieren? Wie kann man Unit Tests implementieren?

    Meine einzige Anregung wäre, dass noch mehr Informationen zu den technischen Hitergründen der Xpages Technologie erwähnt würden.

    Ich bin schon gespannt auf die nächsten Folgen.

    Weiter so!!

    Grüße aus Berlin

    Adrian Stefanescu

    2Ivailo Iliev  09/29/2009 9:34:50  XPages series #6: Tracking data changes

    Hi, this is amazing, thanks for all the articles.

    can you put somewhere here the nsf file so we can have the source- it will be really helpful.

    I have notices something else- sometimes in xPages it's changing automatically the values of EL expressions, and sometimes on 8.5 it's not re-compiling(or re-interpreting) the java changes. I've seen in 8.5.1 it's working more stable, but there is still the problem with re-making the EL strings- sometimes it's deleting them, sometimes it's just changing them. Do you have some experience with this- maybe there is an useful solution how to prevent it :)

    Anyhow- I think this way of development can make Nodes development into much better one and will un-tie our hands :)

    3Karsten Lehmann  09/29/2009 10:09:04  XPages series #6: Tracking data changes

    Hi Ivan,

    I had to delay my further XPage development in the last weeks to work on other projects, but I'm currently coming back to it to design and build a quite large Java architecture for an XPages application (about 10 Java projects to split the architecture up into independent and reusable parts, that don't all require the XPages or JSF runtime in the classpath).

    Regarding the EL expression issue:

    I also saw that in 8.5 and (maybe only partly) fixed in the 8.5.1 code drops.

    John Mackey may have a fix for this in 8.5. He talks about it in his article "XPages - How to pass a Document Data Source to a Custom Control" in the XPages blog at { Link }

    I haven't tried yet if it helps.

    For providing the NSF:

    I think I published all the source files in the blog articles. At the moment, I would consider the sample scenario to be "work in progress", until I decide that the XPages series about leveraging JSF features has ended.

    I think there is still something left to write about, so it will take some time until I can publish a final NSF file. I hope that I will have some new inspiration for articles, now that I'm working again with the technology.

    4Sushant  03/05/2011 12:24:55  XPages series #6: Tracking data changes

    Hi, i am new to Managed Beans and JSF in Xpages. How can we store the data using a managed bean into a Notes Document ? or I want to have the model object ready when the session is going on but how do I commit that to a notes datastore or a Notes Document at the end of session or when I save and submit the Xpage?

    5Reynald Reyes  03/10/2011 2:58:21  XPages series #6: Tracking data changes

    Hi Sushant,

    You can follow the steps used by jeremy hodge in { Link } .. a very detail explanation about committing into the database

    6Karsten Lehmann  03/10/2011 11:52:29  XPages series #6: Tracking data changes

    Hi Sushant,

    I'm on vacation this week. I will post an example how to load/store Lotus Notes data with beans and log data changes next week. This is the third and final example of our Lotusphere session BP212 (slides in this blog).

    Karsten

    7Terese    Mindoo - XPages series #6: Tracking data changes

    8Cheri    Mindoo - XPages series #6: Tracking data changes

    9imoveis aluguel temporada balneario camboriu    Mindoo - XPages series #6: Tracking data changes

    10EM PEDAÇOS FILME DOWNLOAD    Mindoo - XPages series #6: Tracking data changes

    11EM PEDAÇOS FILME DOWNLOAD    Mindoo - XPages series #6: Tracking data changes

    12http://www.desportstore.com/roma-football-shirt-c-germany    Roma Fu?ballshirt

    13http://www.vins-zink.fr/?option=com_k2&view=itemlist&task=user&id=19192    fodboldtr&oslash;jer

    14fotbollströjor    Mindoo - XPages series #6: Tracking data changes

    15http://www.vatal.gr/UserProfile/tabid/43/UserID/241535/Default.aspx    fodboldtr&oslash;jer

    16http://www.asleftasfound.com/    Fodboldtr?jer B?rn

    17http://www.jerseycityvegan.com    maglie calcio

    18clevio    http://www.clevio.biz/xe/?document_srl=125447

    19lovran    http://lovran-apartments.com/groups/a-madrid-jeg-ved-det-ikke-brasilien-vm-troje-var-det-svrt%3fdet-var-e-jeg-vil-spille-i-verdens-storste-hold-hala-madrid-han-svarede-frederico-pena-han-var-klar-han-onsked/

    20http://www.codkahargeysa.com    Billiga Fotbollstr?jor

    21http://www.codkahargeysa.com    Billiga Fotbollstr?jor

    22http://www.glivytech.com    maglie calcio poco prezzo

    23http://www.jerseycityvegan.com    maglie calcio poco prezzo

    24http://www.haeroo.com    maglie calcio

    25http://www.codkahargeysa.com    Billiga Fotbollstr?jor barn

    26http://www.codkahargeysa.com    Billiga Fotbollstr?jor

    27fodboldtrøjer    Mindoo - XPages series #6: Tracking data changes

    28fotbollströjor barn    Mindoo - XPages series #6: Tracking data changes

    29http://www.ladartleague.com#    fotballdrakter

    30fodboldtrøjer    Mindoo - XPages series #6: Tracking data changes

    31http://www.socialworkersguide.com#    fotballdrakter

    32maglie calcio bambini    Mindoo - XPages series #6: Tracking data changes

    33http://www.mydescubrimientos.com#    fotballdrakter ban

    34http://www.doshomi.com#    fotballdrakter

    35http://www.tiendasdecoralia.com#    magliette calcio

    36magliette calcio    Mindoo - XPages series #6: Tracking data changes

    37belgien vm trøje    Mindoo - XPages series #6: Tracking data changes

    38Maglia Belgio mondiali 2018    Mindoo - XPages series #6: Tracking data changes

    39Brasilien landslagetströja    Mindoo - XPages series #6: Tracking data changes

    40Maglia Svezia mondiali 2018    Mindoo - XPages series #6: Tracking data changes

    41England vm drakt    Mindoo - XPages series #6: Tracking data changes

    42Frankrike VM landslagströja    Mindoo - XPages series #6: Tracking data changes

    43belgien vm trøje    Mindoo - XPages series #6: Tracking data changes

    44Belgien vm trøje 2018    Mindoo - XPages series #6: Tracking data changes

    45Sverige landslagetströja    Mindoo - XPages series #6: Tracking data changes

    46Maglia Belgio mondiali 2018    Mindoo - XPages series #6: Tracking data changes

    47Frankrike drakt    Mindoo - XPages series #6: Tracking data changes

    48http://cfbarbertown.phpfox.us/index.php/blog/15087/nicolef-fotbollstr%C3%B6jor-chau/    fotbollstr&ouml;jor

    49http://jc-one.co.kr/xe/index.php?mid=board&document_srl=797869    fotballdrakter

    50http://darkhacker080.cf/profile.php?id=12115    billiga fotbollstr&ouml;jor

    51http://bookmarksclick.com/story.php?title=tottenham-hotspurs-troeja-billiga-fotbollstroejor    billiga fotbollstr&ouml;jor

    52http://lawkor.com/Board/863573    maglie calcio

    53http://itradepro.ru/forum/member.php?u=95475-IJFCecelia    fotballdrakter

    54http://kchurchofchrist.com/index.php?mid=board_XlWR16&document_srl=388523    maglie del calcio

    55http://xn--9e5b62w5qb.com/owaa/498179    maglie del calcio

    56http://kchurchofchrist.com/index.php?mid=board_XlWR16&document_srl=388523    maglie calcio

    57http://dimitrislampos.gr/UserProfile/tabid/43/UserID/275925/Default.aspx    fotbollstr&ouml;jor barn

    58madden mobile slot receiver    Mindoo - XPages series #6: Tracking data changes

    59jal sky suite 777 first class    Mindoo - XPages series #6: Tracking data changes

    60sic bo patterns    Mindoo - XPages series #6: Tracking data changes

    61how to win casino slot games    Mindoo - XPages series #6: Tracking data changes

    62progressive mobile games    Mindoo - XPages series #6: Tracking data changes

    63online casino games    Mindoo - XPages series #6: Tracking data changes

    64m.2 2280 slot for ssd    Mindoo - XPages series #6: Tracking data changes

    65best casino slot game to play    Mindoo - XPages series #6: Tracking data changes

    66rollex11 test id    Mindoo - XPages series #6: Tracking data changes

    67how to hack ocean king game    Mindoo - XPages series #6: Tracking data changes

    68https://is.gd/avksk6    8 Ball Pool Hack

    69scr888 superman    Mindoo - XPages series #6: Tracking data changes

    70lucky palace casino login    Mindoo - XPages series #6: Tracking data changes

    71http://urlku.info/8ballpoolhackcoins831944    8 ball pool hack coins

    72games with progressive scan ps2    Mindoo - XPages series #6: Tracking data changes

    73sky city casino job vacancies    Mindoo - XPages series #6: Tracking data changes

    74seo service    Mindoo - XPages series #6: Tracking data changes

    75https://is.gd/8ballpoolhackonline711895    8 ball pool hack online

    76http://litlurl.net/599307    8 ball pool hack 2018

    77http://qtx.de/8ballpoolcoinsgenerator974978    8 ball pool coins generator

    78https://www.caringbridge.org/profile/40354087    how to hack snapchat

    79http://www.premio-tuning-bestellshop.at/Home/tabid/2115/Default.aspx?returnurl=https://snapfacegram.com/snapchat-hack/    can you hack snapchat

    80web design ui kit    Mindoo - XPages series #6: Tracking data changes