The backing object was divided into two parts: the object itself and its’ storage. This was in order to demonstrate the possibility of using somethig else than NSF as the ultimate data storage facility.
2.1 The object
As mentioned previously, the object implements the com.ibm.xsp.model.DataObject
interface. Here are the functions:
Object getValue(Object object);
This method is called to get the value of a given “key” or binding.
void setValue(Object object, Object value);
This is called to set the value.
boolean isReadOnly();
This tells if the value can be edited. The XPage controls use this e.g. to determine whether an InputText element is rendered as HTML <INPUT> or just text <SPAN>.
That’s really all there is to it.
2.2 The storage
A hashmap with String keys is easy to store in a Notes document. There are two basic options:
1) use the key as NotesItem name and store the data inside the NotesItem. If the values in HashMap are not compatible with Item values, you will need to convert appropriately (you can store Strings, some Numbers and Dates but after that it can get a bit difficult).
2) serialize the hashmap and store the result inside one NotesItem. I have previously implemented such a solution. I quickly ran into problems with item size limits and had to create an abstraction layer, which split the data into multiple items as necessary. The serializable object was written with Item.setValueCustomDataBytes() call.
Here we decided that we need an abstraction layer to hide all the Notes-specific code from the main object. There were multiple reasons including:
- the possibility of switching data storage later (specifically, MySQL)
- this way, the developer of the object doesn’t have to be a Notes developer
- I hope the resulting code will be more maintainable
So, we created an interface, DataStorage
and an implementing class, NotesStorage
. The object can retrieve back-end values and implement business logic like this:
private DataStorage storage = new NotesStorage(); public double getSum() { double price = this.storage.getValue("PricePerKilometre"); int kilometres = this.storage.getValue("Kilometres"); return price * kilometres; }
And the main getter could be something like this:
public Object getValue(Object object) { if (object.toString().equalsIgnoreCase("Sum")) { return getSum(); } else { return this.storage.getValue(object.toString()); } }
Please note that this is not actual code. Monetary amounts should never be passed as floating-point numbers (We have a Money
class which I may blog about later). Anyway, if we later create a SQLStorage
class the change would be “invisible” to this object.
Final words
It has been an interesting learning experience with XPages. It took a long time to “unlearn” the old, Notes way of doing things. The first months were spent trying to re-create Notes design patterns with SSJS, which results in very messy and hard-to-maintain code. You know, buttons with dc.getNextDocument(doc)
loops…
Since I realized that XPages is actually EJB with Notes ACLs and two helper DataSources (just slightly exaggerated), things have been “clicking into place”. So far, these have been the most important steps:
- Switching to only using Java
- Understanding the XPages lifecycle
- Creating your own controls
- Creating your own DataSources