Monday, September 18, 2006

Latest Update to ODM (Object Domino Map)

I've added cache support to the ODM. I decided to use WhirlyCache as the cache API. Of the two I reviewed (JCS and WhirlyCache)... WhirlyCache seemed the easiest to implement and from the API seemed like it kept caching simple. Admittedly I got sucked in because of the "For the impatient, here's how to get started using Whirlycache" article on the homepage of their website :). The ODM has some new methods to cache your java bean objects (see java docs for caching methods i.e. ftQueryForCachedList). Of course w/ the use of this caching API we have added some jar dependencies. In the classpath of your project, or system you will need to add the following jars:

  • ODM.jar
  • NCSO.jar (lotus/domino java APIs, not included in download... can be downloaded from lotus.com)
  • whirlycache-1.0.1.jar
  • commons-collections-3.1.jar
  • commons-logging.jar
  • concurrent-1.3.4.jar
  • log4j-1.2.8.jar
Download from my Box.net share

For more info on ODM see the Using a Domino Database to store Java Beans

Saturday, August 05, 2006

Using a Domino Database to store Java Beans

Lotus Domino developers have long enjoyed the benefits of a flexible persistence layer that comes out of the box. The .nsf database, its surrounding APIs, full text indexer, and world class replication make it a very good candidate for creating a persistence layer for your java objects (java beans) across your company or enterprise. The purpose of this article is to demonstrate Lotus Domino's database flexibility with a thin data layer API that I decided to call Object Domino Map (ODM... very creative I know ;-). I was inspired to build ODM after working with a great open source tool called ibatis. I used ibatis to persist java objects to a relational database and thought that Domino's database would have a definite advantage in some ways over an RDBMS for storing objects.
One of the key factors of storing objects is Domino's database flexibility. Developers don't have to worry about mapping column names to java bean field attributes, nor do they have to be concerned about changing those attributes over time. Domino dynamically creates the fields for the developer as needed, and maintains any unused attribute in the document record. As the application's object model matures and the underlying java beans that persist the data change, the Domino database will keep everything including old attributes if needed. The rest of this article will focus on using the ODM API through setup and example.

What you need to get started:
  • Lotus Domino Server running DIIOP(free trial of the server at lotus)
  • Create an empty database on the root called books.nsf (FT Index the database)
  • A Java development environment setup
  • (latest version is in another post containing cache implementation)
    • Extract the .zip file and import the ODM.jar, source, and example code into a new project in your favorite IDE.
    • If you have any issues downloading please send me email (see end of article for address) and I will respond w/ .zip attachment.
  • Be sure to add NCSO.jar and the ODM.jar to your classpath/project libraries.
Configuration:
At the root of your source folder in the examples (default package) you should see two .properties files: odmcfg.properties, and books.properties. Go into the odmcfg.properties file and alter the settings for your environment. Below are a listing of the properties and their descriptions:

odmcfg.properties:
  • DOMINO_SERVER=
    • Domino server that is hosting your database
  • USER_NAME=
    • username used to login to the server
  • PASSWORD=
    • password used to login to the server
  • NUM_SESSIONS=5
    • number of sessions the DominoSessionFactory will produce
  • SESSION_TICKLE_INTERVAL=5
    • interval the sessions in the DominoSessionFactory are kept alive (calls isOnServer from Domino's session)
  • TARGET_DATABASE=books.nsf
    • nsf path used to store your java bean objects
  • com.odmexample.Book=book.properties
    • a pointer to the properties file containing object/class specific settings (you must register each of your beans in the odmcfg.properties file)


class specific properties files (i.e. com.odmexample.Book=book.properties)
  • objectNoteId=uniqueId
    • objectNoteId (required) is used to get a handle on the java bean using a pre-determined unique Id called noteId (note you can also use objectUNID to use the universal id across replicas).
  • onSave_dtModified=@Modified
    • onSave_attribute will evaluate a LotusDomino formula when the NEW object is saved to the database
  • onUpdate_dtModified=@Modified
    • onUpdate_attribute will evaluate a Lotus/Domino formula when a pre-existing object has been updated
  • onGet_dtCreated=@Created
    • onGet_attribute will evaluate a Lotus/Domino formula when the java bean object is retrieved.


Running the Example:
  • Go to the java class com.odmexample.CreateBooks and execute the file. This will create 100 objects (documents) in the book.nsf database using the attributes in the com.odmexamples.Book java bean as the field names.
  • Update the FT index manually (ODM supports both dbSearch, and FTSearch, but this example uses FT Index)
  • Go to another java class file com.odmexample.ViewBooksByAuthor and execute this file. It should print to the screen any authors with the name SpongeBob1 or the arg you specify (SpongeBob1, SpongeBob2 etc ;-).

Other features of ODM/Domino:
  • ODM supports the storage of primitive arrays like double, long, int and can also store arrays of strings.
  • ODM also supports the storage of any object attribute in your bean that implements serializable. The downside is that you can't search based on the object's info, and the serialized object cannot exceed 64kb.
  • File attachment support using saveWithFileAttachment option(attach any type of file to the object, file name and meta data should be stored w/ your bean attribute i.e. getFileName, getFileType...)
Possible Future Development:
  • Simple cache model of java beans and collections (will probably borrow from apache)
  • Instead of direct field access to the beans use method invoke reflection calls (since I'm violating rules of encapsulation ;-)
  • Review other means of processing beans without use of reflection, or one time reflection via code generation and compilation.
  • Change configuration files from .properties to xml (although I kind of like simplicity that property files bring)
  • Better logging (have LogUtils class (see source docs) but currently not using)

Conclusion:
Lotus/Domino's datastore is one of the most flexible around and is fully owned and supported by IBM, making it a viable alternative to RDBMS for storing objects in most applications. The ODM API is available via the GNU General Public License. This is the first version of the API if you find any bugs or have questions please feel free to send me email(see my profile).

Friday, May 19, 2006

Using LotusScript to import an image resource


A few years back I wrote some code and co-authored an article on using the Java and DXL Toolkit to import image resources into the domino design. I figured as my first blog entry I'd write about the same subject utilizing the latest LotusScript APIs in release 6x to import an image resource into a Lotus Domino design...

We start with a simple notes form with a single file upload control and a submit button that invokes an agent on submit.

The agent code below handles importing the imageSub Initialize
On Error Goto MyErr

Dim s As New NotesSession
Dim db As NotesDatabase
Dim col As NotesDocumentCollection
Dim doc As NotesDocument
Dim fileNames As Variant Dim item As NotesRichTextItem
Dim bean As FileBean
Dim factory As New FileBeanFactory()
Dim resource As ImageResource


Set db = s.CurrentDatabase
Set doc = s.DocumentContext

'creates the object collection of FileBeans.
Call factory.createFileBeans(doc)
'now get the bean based on one of the file names in the doc
fileNames = Evaluate("@AttachmentNames",doc)
'get the FileBean object from the factory using a file name as the key
Set bean = factory.getFileBean(fileNames(0))
'set the resource to a new image resource based on FileBean
Set resource = New ImageResource(bean)
'save the image resource to database (note can be any database)
Call resource.saveToDatabase(db)

Exit Sub

MyErr:
Dim errBody As String
errBody =Lsi_info(2) & " " & Str(Err) & ": " & Error$ & " on line: " & Erl()
Print errBody

Exit Sub



End Sub

In the above agent you will notice that I've created three custom classes to facilitate the importation of the image resource:


  • FileBean (temporary data store for a single attachment containing the base64 encoded file data)
  • FileBeanFactory (factory class that creates a FileBean per attached file in the document using DXLExporter and SAX parser to create a collection of FileBean(s))
  • ImageResource (class that takes the bean and does the import using the DXLImporter to passed in database)

In a nutshell this is pretty much it. There is a BeanFactoryHelper class that is wrapped by FileBeanFactory. If you are interested in seeing how the FileBean is built using the SAX parser look there. There is also a handy collections object I've built and use in most of my apps. It is sort of a hybrid between a HashMap and an ArrayList.... containing methods to get object handles via index, and string keys along with some enumeration helpers like getFirst, getNext, getNth(i) etc... To try out the app download .zip and extract the .nsf to you domino server. Once setup on domino launch the Upload_Image?OpenForm url command on your development domino server using http://yourhost/ImageResourceImport.nsf/Upload_Image?OpenForm

Download