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

  • Entwicklercamp 2013 slides about "Dojo 1.8 and AMD" now available in English

    Karsten Lehmann  24 May 2013 20:48:25
    My slides about "Dojo 1.8 and AMD" are now available in English. I have updated my original blog article:


    My EntwicklerCamp 2013 slides: Dojo 1.8 and AMD

    Karsten Lehmann  13 March 2013 14:53:59
    Here are the slides for my EntwicklerCamp 2013 session about "Dojo 1.8 and AMD" in English:

    Image:My EntwicklerCamp 2013 slides: Dojo 1.8 and AMD


    and in German

    Image:My EntwicklerCamp 2013 slides: Dojo 1.8 and AMD


    Download archive with both versions:

    Slides as ZIP-Archive

    Quick tip: Fixing Dojo drag and drop issues in a Windows 8 VMWare on the Mac

    Karsten Lehmann  7 March 2013 07:33:56
    I am currently setting up a new dev environment with Windows 8 and Notes/Domino 9 to work on demos for my Dojo 1.8/AMD session at Entwicklercamp next week.

    To my surprise, I noticed yesterday, that drag and drop operations on Dojo widgets did not work as expected. For example, I could not drag the splitters of a BorderContainer layout widget and the columns of a LazyTreeGrid could not get resized.
    It seemed as if mouse events got lost, but I only got that effect in Firefox and Chrome. In IE, everything was working fine.

    After a bit of Googling, I found out that VMWare Fusion emulates some kind of touch device for Windows 8 and that this can get disabled by setting

    touchscreen.vusb.present = "FALSE"

    in the VMX file of the VMWare image. I tried it and it helped, drag and drop is now working again.

    Download links for IBM Connect 2013 session slides

    Karsten Lehmann  31 January 2013 09:12:53
    As in previous years, I copied the download URLs of all the already available session slides from the socialbizonline.com website.
    Unfortunately, a lot of slides are still missing and some are only provided in black and white mode with 2-on-1 page.

    Use your preferred download manager to download the files. I use DownThemAll for this purpose. You need to be logged in to the website to access the files.

    Here are the download links:

    Connect2013_PDFs.html

    And here is the spreadsheet I used to format the links:

    Connect2013_PDFs.ods

    Have fun with it on your flight back home!


    Fast Notes view reading via Java API:New ViewNavigator cache methods in 8.5.3

    Karsten Lehmann  17 January 2013 08:53:08
    Preface

    A posting about new API methods in 8.5.3 may look a bit weird, now that the Notes/Domino R9 beta is already out for one month. I wanted to blog about this topic for one year now, actually since last year's Lotusphere conference, where the new API methods got presented by IBM (in session "AD112 What's new in the IBM Lotus Domino Objects: Version 8.5.3 in Demos"), but could not find the time and have always expected that IBM dev would write a wiki article about it - which they haven't so far.

    Let's get started!


    This blog article is about getting better view reading performance for both Java and LotusScript based applications. As discussed back in 2010 in two blog posts

    Fast Notes view reading via C API: A comparison C vs. Notes.jar
    Fast Notes view reading via C API Part 2: How to read views 99.7% faster than Notes.jar

    it is possible to get an extreme performance boost traversing view data by using raw C API methods instead of Java/LotusScript, which both bypass ECL checks that cost performance and in addition they are able to fetch multiple view entries at once (in one server transaction) instead of one at a time.

    Not everyone likes developing C code and compiling it for each platform an application should run on. So IBM decided to add a few methods to the 8.5.2 Java API so that everyone can get better performance.

    Most of the improvements have been documented in the Designer wiki article Fast Retrieval of View Data Using the ViewNavigator Cache - V8.52.
    Improvements have been made in the ViewNavigator class so that the methods ViewNavigator.getNext() and ViewNavigator.getPrev() can use an internal cache to preload up to 400 view entries.

    All you have to do is:

    //decide how many entries should be preloaded
    int numCacheEntries=400;

    //get a view from a database
    View lookupView=db.getView("myview");

    //disable auto updating
    lookupView.setAutoUpdate(false);

    //get a ViewNavigator instance for all the view entries
    ViewNavigator nav=lookupView.createViewNav();

    //and set the size of the preloading cache:
    nav.setBufferMaxEntries(numCacheEntries);

    //and then traverse the view:
    ViewEntry currEntry=nav.getFirst();
    while (currEntry!=null) {
           if (currEntry.isValid()) {
                   doSomethingWithTheEntry(currEntry);
           }
           ViewEntry nextEntry=nav.getNext();
           //don't forget to recycle!
           currEntry.recycle();
           currEntry=nextEntry;
    }

    nav.recycle();
    lookupView.recycle();


    This sample code reads all the entries of a view from start to end. For categorized views, entries get read as if they all were expanded, so we read parent entries first, then their child entries, next the children's child entries.

    Reading subsets of a view


    If reading the whole view is too much, you might just as well read a specific view category:

    ViewNavigator subCategoryNav=lookupView.
                            createViewNavFromCategory("topcategory\\subcategory_level1");


    or read unread documents:

    ViewNavigator unreadEntriesNav=lookupView.createViewNavFromAllUnread("Firstname Lastname/Company");

    or start reading document entries beginning with a specific letter (first column must be sorted, e.g. by lastname):

    ViewEntry firstEntry=lookupView.getEntryByKey("L", false);
    if (firstEntry!=null) {
            ViewNavigator subEntriesNav=lookupView.createViewNavFrom(firstEntry);
           
            //we use a cache size of 50; use more if you expect more results
           subEntriesNav.setBufferMaxEntries(50);

            //traverse the view:
           //...

            firstEntry.recycle();
            subEntriesNav.recycle();
    }


    Undocumented feature: ViewNavigatort.skip(int entriesToSkip)


    There is one more new method in 8.5.2 that is missing in IBM's wiki article:

    int start=50;

    //try to skip 50 entries and return the entries actually skipped
    int skippedEntries = viewNav.skip(start);

    if (skippedEntries==start) {
            //read the current entry after the skip operation
            ViewEntry currEntry=viewNav.getCurrent();

            //work with currEntry
            //...

            currEntry.recycle();
    }


    This method skips a number of entries from the current position (e.g. from the first entry) and is very fast, because view traversal is done in C and no column data is decoded. It returns the number of entries that were actually skipped.
    If skippedEntries is less than start, the view does not contain enough rows, so there is nothing relevant to read.

    The skip method is used in the XPages Extension Library to find relevant data for a view navigator based REST service: the code first skips "start" entries and then reads the next "count" entries from the view and returns their data as JSON.

    Another use case for this method is counting view entries:
    The funny thing is, that both ViewNavigator.getCount() and ViewNavigator.skip(Integer.MAX_VALUE) both the return the same count, but the latter is much faster.

    New in 8.5.3: Better performance for other traversal methods

    While 8.5.2 only provided improved performance for ViewNavigator.getNext() and ViewNavigator.getPrev(), 8.5.3 adds better performance for all the other traversal methods, e.g.

    public ViewEntry getNextSibling();
    public ViewEntry getPrevSibling();
    public ViewEntry getNextCategory();
    public ViewEntry getPrevCategory();
    public ViewEntry getNextDocument();
    public ViewEntry getPrevDocument();
    public ViewEntry getParent();
    public ViewEntry getChild();


    To activate the cache, there is a new method in ViewNavigator that defines the cache size and the cache filling strategy:

    public void setCacheGuidance(int maxEntries, int readMode);

    which replaces the previous setBufferMaxEntries method (you can still use the old method, but the new method can do the same and more).

    readMode can either be the constant

    ViewNavigator.VN_CACHEGUIDANCE_READALL
    or
    ViewNavigator.VN_CACHEGUIDANCE_READSELECTIVE.

    With the first constant, the ViewNavigator fills the cache like it did in 8.5.2, that means it preloads the next maxEntries entries of the view as if a categorized view would be totally expanded.

    The second constant however fills the cache with entries based on the getter method that you call next (see examples below; you can reset the cache size and mode at any time).

    Reading category entries

    So to quickly read all categories of a view, you could do

    //decide how many entries should be preloaded
    int numCacheEntries=400;

    //get a view from a database
    View lookupView=db.getView("myview");

    //disable auto updating
    lookupView.setAutoUpdate(false);

    //get a ViewNavigator instance for all the view entries
    ViewNavigator nav=lookupView.createViewNav();

    ViewEntry currEntry=nav.getFirst();

    if (currEntry!=null) {
            if (!currEntry.isCategory()) {
                    //make sure currEntry is a category or null
                    ViewEntry firstCategory=nav.getNextCategory();
                    currEntry.recycle();
                    currEntry=firstCategory;
            }

            //set cache strategy to optimize getNextCategory() calls,
            //fills the cache with the result of 400 getNextCategory() calls
            nav.setCacheGuidance(numCacheEntries, ViewNavigator.VN_CACHEGUIDANCE_READSELECTIVE);

            while (currEntry!=null)  {
                    doSomethingWithTheCategory(currEntry);

                    //move to next category; refills the cache after 400 categories
                    ViewEntry nextEntry=nav.getNextCategory();
                    currEntry.recycle();
                    currEntry = nextEntry;
            }
    }
    nav.recycle();
    lookupView.recycle();


    Reading direct child entries

    In our web development projects, the most important use case for the new cache support in 8.5.3 is to read the direct child entries in a view.
    We often do this to populate a lazy loading JavaScript tree or tree table: When the user expands a tree entry, the next tree level gets loaded and displayed.

    This is demonstrated in the following snippet. parentEntryPos would contain the position of the expanded parent entry.

    //get a view from a database
    View lookupView=db.getView("myview");

    //disable auto updating
    lookupView.setAutoUpdate(false);

    //position of parent view entry to read direct child entries or null to load the first level
    String parentEntryPos="3.1.2";

    //get a ViewNavigator instance for all the view entries
    ViewNavigator nav=lookupView.createViewNav();

    boolean foundFirstEntry=false;
    if (parentEntryPos!=null && !"".equals(parentEntryPos)  {
            if (nav.gotoPos(parentEntryPos, '.')) {
                    if (nav.gotoFirstChild()) {
                            foundFirstEntry=true;
                    }
            }
    }
    else {
            if (nav.gotoFirst())
                    foundFirstEntry=true;
    }

    if (foundFirstEntry) {
            //get the first entry to read:
            ViewEntry currEntry=nav.getCurrent();

            //get the maximum number of siblings to find the right cache size:
            int maxNumSiblings=currEntry.getSiblingCount();

            //set cache strategy to optimize getNextSibling() calls
            //fills the cache with the result of maxNumSiblings getNextSibling() calls
            nav.setCacheGuidance(maxNumSiblings, ViewNavigator.VN_CACHEGUIDANCE_READSELECTIVE);

            if (currEntry!=null) {
                    while (currEntry!=null)  {
                            //not 100% sure that the user can see the child entries;
                            //currChildCount may contain hidden documents:
                            int currChildCount=currEntry.getChildCount();
                            boolean hasChildren=false;
                            if (currChildCount > 0)
                                    hasChildren=true;
                           
                            Read the position of the current entry in the view:
                            String currEntryPos=currEntry.getPosition('.');

                            doSomethingWithTheSibling(currEntry, currEntryPos, hasChildren);

                            //move to next sibling
                            ViewEntry nextEntry=nav.getNextSibling();
                            currEntry.recycle();
                            currEntry = nextEntry;
                    }
            }
    }

    nav.recycle();
    lookupView.recycle();


    Of course, the code can be further improved to ignore the first start entries and stop after reading count entries.

    Unfortunately, there is no method skipSiblings(int siblingsToSkip) in ViewNavigator to quickly skip sibling entries in a view level. We requested this as a feature, but things are not moving very fast in this area of the product. ;-)

    Child and sibling counts in Domino: an eye opener

    When you look at that last snippet, you might wonder if reading the sibling or child count and the entry position is a wise decision.
    Same did I at first. I thought reading count information of ViewEntries would probably take a long time, because Domino has to go through all the entries to calculate the right amount.

    The good news is:
    Reading child and sibling counts in Domino is actually very fast.

    The bad news is:
    The count returned is not exactly what you might expect. :-)

    I guess a picture is worth a thousand words:

    Image:Fast Notes view reading via Java API:New ViewNavigator cache methods in 8.5.3

    What you see is a view in Sean Cull's Fakenames database with 40.000 fake user records. I added columns for both the child count and the entry positions.
    Domino returns a child count of 8, but we can only see 6 entries and there seem to be gaps in the view: the entries 1.4 and 1.5 are missing.

    The reason is that I added reader fields to those documents, so that I'm not allowed to see them anymore.

    And since checking if a user is allowed to see a document is a pretty heavy operation of comparing reader fields, the user's roles in the database's ACL and the group documents in the server directory, Domino does not do this just to calculate a count.
    Domino simply returns a count value instead, that is stored in the view index and is only a maximum of available child/sibling entries.
    It's the amount of entries the server can see.

    Please note that reading count values is not always a cheap operation. When working with ViewEntryCollection, DocumentCollection or NoteCollection, Domino will go through all entries in many cases, which takes a lot of time.

    Troubleshooting cache usage

    If you are not sure if your cache settings are actually used, you can enable client_clock debugging via Notes.ini as described here or here (in German language).
    While traversing view entries, you should see much less RPC calls to the server when using the cache than without using it.

    Backward compatibility

    If you want to use the new cache guidance functionality, but your code needs to run on older Domino servers as well, you should not call the new method directly. Your code would not compile in older versions and throw an exception when it runs.

    Use Java reflection instead to check if setCacheGuidance actually exists:

    public static boolean setCacheGuidance(ViewNavigator viewNav, int maxEntries, int readMode) throws NotesException {
            try {
                    Method setCacheGuidance=ViewNavigator.class.getMethod("setCacheGuidance", Integer.TYPE,
                                                    Integer.TYPE);
                    setCacheGuidance.invoke(viewNav, maxEntries, readMode);
                    return true;
            }
            catch (SecurityException e) {
                    //ignore, must be 8.5.2 or below
            }
            catch (NoSuchMethodException e) {
                    //ignore, must be 8.5.2 or below
            }
            catch (IllegalArgumentException e) {
                    //ignore, must be 8.5.2 or below
            }
            catch (IllegalAccessException e) {
                    //ignore, must be 8.5.2 or below
            }
            catch (InvocationTargetException e) {
                    if (e.getCause() instanceof NotesException)
                            throw (NotesException) e.getCause();
                    //ignore, must be 8.5.2 or below
            }
            return false;
    }


    ViewEntryCollection improvements?

    Talking about ViewEntryCollection, there have been plans to add cache support in this class as well. This would be useful to quickly read the results of a fulltext search (e.g. up to 5000 entries by default).
    Unfortunately, the current R9 beta does not contain anything new in this area, so there is no big hope that it will get added in R9.

    More on this topic

    If this topic is interesting for you and you attend IBM Connect 2013, mark this session in your schedule:

    AD211 - What's New In The IBM Domino Objects?
    Elizabeth Sawyer - IBM; James Cooper - IBM

    The Domino Objects for Release 9.0 debuts a major set of classes for Calendar & Scheduling as well as additional performance features for View Navigation.  Join us as we explore the powerful new capabilities coming soon to application developers through discussion and demonstration of  representative use cases.


    I'm pretty sure that they will also demonstrate something new in the R9 API, for example public Vector getColumnValues(int columnNumber) in the View class that you can find in the beta version and that should work like @DbColumn. :-)


    That's it for today. I hope this article is helpful and you can speed up your applications!


    Our session got accepted for IBM Connect: BOF211 Leverage OSGi plug-ins in Your XPages Applications!

    Karsten Lehmann  9 January 2013 08:20:41
    Today we got the information that our Birds of a Feather session submission (BOF) has been accepted for IBM Connect 2013.

    Here is the abstract:

    BOF211 Leverage OSGi plug-ins in Your XPages Applications!

    Tammo Riedinger, Mindoo GmbH; Karsten Lehmann, Mindoo GmbH


    Hear how XPages apps can be extended with your own visual controls and data can be leveraged from external databases! We want to talk about code sharing of libraries between multiple XPages apps, Notes Client plugins and standalone applications.We also want to discuss, how custom server processes and even complete applications can be added to IBM Lotus Domino on top of OSGI, due to its highly modular and extensible nature. The only limit is your imagination!



    That's quite similar to the description that we had submitted as a normal session, which unfortunately did not get accepted by the track managers. BOF sessions however get voted by the conference attendees.

    What you don't get in our BOF session compared to the normal one:
    • slides
    • demos

    What you will get in our BOF session compared to the normal one:
    • discussion
    • exchange of experiences

    and hopefully a small breakfast buffet with coffee in front of the room, in case we get a 7am slot for the BOF (hear us IBM? We once did a BOF with no breakfast, that was hard :-)  ).

    This is how IBM describes Birds of a Feather sessions:
    • A Connect 2013 BOF is a relaxed,  interactive discussion, with no media or AV. This is a 'chalk-talk' style meeting, and the purpose of the discussion is to gather input, feedback, and learn from each other. Again, no room AV is to be used, no exceptions please.
    • The role of the facilitator is to do just that -- facilitate discussion. All in attendance should be given an opportunity to become engaged. This is not a 'one to many' session. Remember -- at Connect, "Everyone has a voice" !
    • Flip-charts and markers will be provided upon request. Any other session tools, props, etc are the responsibility of the facilitator(s).

    Great to be part of the conference! See you in Orlando!

    XPages series #15: Free FTP server on top of Domino’s OSGi framework

    Karsten Lehmann  3 November 2012 14:14:02
    Last week we had a conference call with IBM. They provide free VMware images of several IBM products for business partners to be used for demo and development purpose, including IBM Connections 4, already set up for the well known Renovations company.
    Those images very pretty large, with several GB's of data and the fastest and easiest way to get our hands on the images was to set up an FTP server and have the IBM'ers upload the files.

    That was a good occasion to build a small proof-of-concept that I had already thought of 1-2 years ago:

    An FTP server running on top of Domino


    The implementation is actually quite easy: We simply take an existing FTP server and integrate it into the OSGi framework of the Domino server as a plug-in.
    I selected the Apache FtpServer for this purpose, because it's available as open source with Apache License and easy to set up.

    The first task was to find all necessary libraries to run the server. The FtpServer project is available via Apache's Maven repositories. Maven is a powerful build system that takes care of all code dependencies.
    Although Maven can often be a real PITA, creating a build means basically just to download the source code and use the command

    mvn clean install

    or

    mvn clean install -DskipTests

    The option "-DskipTests" skips the automatic test case execution during the build process and reduces build time.

    As tweeted a few months ago, there is a way to tweak Maven's pom.xml file to let it generate all required Jar files and copy them to a target directory.
    You just need to add this snippet to the pom.xml file:

    <plugin>
          <artifactId>maven-dependency-plugin</artifactId>
          <executions>
                  <execution>
                          <phase>install</phase>
                          <goals>
                                  <goal>copy-dependencies</goal>
                          </goals>
                          <configuration>
                                  <outputDirectory>${project.build.directory}/lib</outputDirectory>
                          </configuration>
                  </execution>
          </executions>
    </plugin>


    I then copied all generated Jar files into a new plug-in:

    Image:XPages series #15: Free FTP server on top of Domino’s OSGi framework




    and exported the relevant packages in the plug-in's manifest file:

    Image:XPages series #15: Free FTP server on top of Domino’s OSGi framework



    Next, I created a plug-in "com.mindoo.ftp" that uses the code from "org.apache.ftpserver" and creates the actual FTP server instance.
    It reads the server configuration and user account data from a small Notes database and writes log documents into another one.

    For now, the FTP server reads and writes its files from disk and not from document attachments in NSF databases, although that would be technically possible and might be an option for the future.

    I decided not to use NSF for the files, because a) this is just a proof-of-concept and b) the FTP server should be able to resume uploaded and downloaded files. The standard Notes Java API does not provide any real streaming functionality (EmbeddedObject.getInputStream() internally just writes the file to disk first, which would kill performance).

    Streaming of attachments out of NSF is possible via NSFNoteExtractWithCallback in the C API (we used this in the MindPlan application to let the user cancel attachment extraction) and there may be a similar method to import attachments via stream.

    Server Configuration


    Our configuration database looks like this - nothing fancy yet:

    Image:XPages series #15: Free FTP server on top of Domino’s OSGi framework



    I added fields for the most relevant server settings to the configuration form, e.g. the server's TCP/IP port, a blacklist to block IP addresses and subnets as well as parameters to use the server behind a NAT router (I tried running it from inside a VMware image).

    Image:XPages series #15: Free FTP server on top of Domino’s OSGi framework



    For users, we can configure their home directory, set a password (we only save a one-way hash generated by @Password) and set a few options to limit the user's access to the server, e.g. define an access period and remove write permissions.

    Image:XPages series #15: Free FTP server on top of Domino’s OSGi framework


    Running the server


    The FTP server plugins should be deployed into the Domino server's HTTP task OSGi framework and start when the HTTP server gets started.
    Unfortunately, in 8.5.3 this is not that easy. There is no such extension point that can be used to automatically start a plug-in on HTTP task startup, like org.eclipse.ui.startup for RCP applications (we have an open feature request for 8.5.4). Everything is started lazily when it is used for the first time. Bad for a server that needs to listen for incoming connections on a TCP/IP port.

    However, the XPages runtime provides one extension point that we can use:

    <plugin>
           <extension
           point="com.ibm.commons.Extension">
                   <service
                   class="com.mindoo.ftp.startup.StartupHandler"
                   type="com.ibm.designer.runtime.extensions.RuntimeInitializationEvent">
                   </service>
           </extension>
    </plugin>


    This way, our plugin gets started the first time an XPage is called. That's a first step, but we need to automate this XPage invocation somehow, to be sure that the server is running after the HTTP task or the whole server is restarted.

    Fortunately, tweeting about this issue helped. Thanks to René Winkelmeyer, I could come up with a solution that leverages the XPages preload feature of Domino 8.5.3 to automatically start the FTP server plugin.
    All you have to do is add the following lines to the server's Notes.ini:

    XPagesPreload=1
    XPagesPreloadDB=mindoo-ftp-config.nsf/ftplauncher.xsp


    "ftplauncher.xsp" is a small dummy XPage in our configuration database. You can use any other XPage instead, since the purpose is just to start the OSGi framework.

    DOTS


    There is an alternative OSGi implementation that can run as part of the Domino server installation: DOTS, also known as Domino Tasklet Service for IBM Lotus Domino.
    In constrast to the HTTP OSGi framework, DOTS contains an extension point to launch plug-ins on framework startup.

    I did not choose DOTS to run the FTP server, because as of 8.5.3, DOTS is not part of the Domino server and needs to be deployed manually. In addition, by using the HTTP OSGi framework instead of DOTS, we can directly call plug-in methods from XPages applications, which might be useful for some applications.

    Server console commands


    I added three server console commands to control the FTP server from the server console:

    tell http osgi mftp restart  -  stops and starts the server
    tell http osgi mftp stop  -  stops the server
    tell http osgi mftp start  -  starts the server


    And of course, the FTP server gets restarted on

    restart task http

    which restarts the whole HTTP task.

    Adding and modifying users does not require a server restart. The lists of blocked IP's and subnets in the server configurations does also get automatically re-read when somebody tries to log into the server.
    All other options currently require a restart of the FTP server.

    Free as in beer


    This proof-of-concept is made available for free. I am sure there are other cool use cases for OSGi based background tasks in Domino. In particular, a scheduler plug-in might be a good thing.

    Setup instructions


    The download archive contains an update site with the plugin code that needs to be deployed onto the Domino server just like the XPages Extension Library : create an NSF update site database, import the plug-ins and features, configure the Notes.ini variable OSGI_HTTP_DYNAMIC_BUNDLES and restart the HTTP task.

    The default paths for the configuration database is "mindoo-ftp-config.nsf" and "mindoo-ftp-config-log.nsf" for the log database.

    An alternative configuration database path can be configured via the Notes.ini variable "$mndFTPConfig=configdb.nsf". For the log database, the variable is called "$mndFTPLog=logdb.nsf".

    And finally, the base directory of the FTP server can be set via "$mndFTPBaseDir=c:\temp\mftp".

    Hope this tool is useful!

    Here is the download link:
    mftp.zip


    Session accepted for Entwicklercamp 2013: Dojo 1.8 and AMD

    Karsten Lehmann  30 September 2012 21:28:24
    I just received confirmation that my session submission for next years developer conference Entwicklercamp 2013 in Gelsenkirchen, Germany (11th - 13th of March) got accepted.

    The session is about the new Dojo toolkit 1.8 that we have already used in a customer project since its release in August 2012.
    Sessions at Entwicklercamp are held in German language, so here is the translated abstract and the original one in German.

    Dojo 1.8 and AMD


    The session provides an introduction to web application development with version 1.8 of the Dojo toolkit.

    It covers available UI elements for web and mobile development, declarative and programmatic web ui design as well as the new AMD module system, that can be used to build highly modular web applications.

    With short demos, additional tools will be presented, that can be used to impress customers and colleagues.


    Dojo 1.8 und AMD


    Die Session gibt einen Einstieg in die Webapplikationsentwicklung mit dem Dojo Toolkit in der Version 1.8.

    Behandelt werden die zur Verfügung stehenden grafischen Elemente für Web und Mobile, deklarative und programmatische Weboberflächengestaltung sowie das neue AMD-Modulsystem, mit dem sich äußerst modulare Applikationen aufbauen lassen.

    Im Rahmen von Kurzdemos werden darüber hinaus noch weitere Tools vorgestellt, mit denen man Kunden und Kollegen zum Staunen bringen kann.

    Customer project: Dojo 1.8 based portal on top of XPages and Domino 8.5.3

    Karsten Lehmann  18 September 2012 00:13:00
    The last months have been incredibly busy for us at Mindoo and I could not find much time for blogging. Tweeting about my findings on the web is just so much easier than to write a complete blog article.

    We spent most of our time working on web applications for desktop browsers and the iPad (including Retina support), based on our favorite web toolkits Ext.JS from Sencha as well as the Dojo toolkit.
    Since we like cutting edge development, we prefer to use the latest versions of toolkits. For Sencha Touch, this means 2.0.x, Ext.JS is available in version 4.1.x and for Dojo it's the brand new version 1.8 that we already used for a portal framework development during the past 3 weeks.

    Portal framework


    This portal framework is a customer project, so there is not much code I can share (I will try to write blog entries about specific technologies).
    But I can describe the basic ideas in this blog post and provide some links to useful toolkits that helped us building the app.

    The portal leverages Dojo's dojox.layout.GridContainer (click here for a portal sample app) to build the portal UI. Portlets can have two different render modes: IFrame and inline rendering. In IFrame rendering mode, the portlet content is completely independent from the parent page; in inline rendering mode, the portlet code can directly render its content into the container node of a dojox.widget.Portlet within the portal page.

    The portal page acts as a communication mediator between the portlets (both with IFrame and inline rendering). Portlets can send JSON based messages to the portal, which - depending on the message content - can send a response to the portlet (e.g. to return loaded portlet state information), forward the message to another portlet (to update another portlets state) or send out a broadcast message to all portlets in the page (e.g. to publish selection changes in a Dojo grid).

    More or less, the portal acts like a Composite Application on steroids. :-)
     
    Cross-domain security


    While sending messages between portlets with inline rendering is quite easy, we needed to find a way to cross the IFrame boundaries in order to send data from IFrame content to the main page back and forth.
    This is not that easy, because browsers prevent this kind of communication to avoid cross-domain security issues.

    Fortunately, there is easyxdm, a Javascript library that contains several solutions for cross-domain communication and automatically picks the right one for the current browser.

    Portlet content rendering


    Loading the portlet content for IFrame rendering mode is quite easy. All you need to do is create an IFRAME node with JavaScript and set its src attribute to the remote content URL.
    This way, portlet content can get generated in external databases or even on different servers (including servers that are not Domino based).

    This works quite well on desktop browsers. They have a good internet connection and plenty of RAM, so it's not a problem to load newer or older Dojo versions or other toolkits as part of the IFrame content.
    Scrolling content that is larger than the portlet viewport is quite easy as well. This is all handled automatically by the desktop browser.

    Unfortunately, this approach did not work well on mobile devices. Scrolling content with a Swipe gesture did not work at all and we noticed rendering issues: in many cases, the IFrame content did not render properly.
    That lead us to the development of inline rendering mode: The whole portal content is stored in one DOM tree and shares the portal's Dojo 1.8 framework.
    This is good and bad at the same time: Page load is faster, but portlets needs to be developed in a way that they don't interfere with each other and only load required resources once (like JavaScript code or CSS files).

    In addition to adding the inline rendering mode, additional tweaks had to be applied to the portal rendering in order to support drag&drop of portlets on the iPad (Dojo's GridContainer layout is pretty hardcoded to mouse events) and change the portal column width with a pinch gesture.
    To drag&drop portlets, we implemented a special edit mode, comparable to the "jiggle mode" in the springboard on iOS devices: Portlets in edit mode get a CSS based jiggle animation to illustrate that they can get moved with a swipe gesture (tap on the portlet and drag it to the target location).

    Dynamic classloading

    To implement the inline rendering mode of portlets in a clean way, we built the whole portal framework on top of Dojo's new AMD classloader architecture. This lets us define classes, implement class inheritance and lazily load portlet code so that it gets loaded when it is required for the first time, speeding up overall page load.
    By following a tutorial on the Dojo website, we could also load specific classes from different locations than the portal page itself. This enables us to separate the portal code from the actual portlet code, making the application reusable and easy to extend.

    In addition, we used the Ensure library to coordinate the loading of resources that are required for the portlet rendering process: Portlets send a JSON message to the portal with a list of required HTML, JS and CSS files and the portal only loads files that have not been fetched for other portlets before.
    When all resources are loaded, Ensure calls our callback function which then notifies the portlet that all resources have been loaded and content rendering can start.

    Modality


    Portlets can be configured to be modal. A modal portlet gets displayed in a Dojo dialog on desktop browsers (including dialog-in-dialog) and gets opened in a separate dojox.mobile.View with transition effect on the iPad in order to use all the real estate on the iPad screen. Since portlets can exchange data via the portal page mediator, this can be used to provide special settings portlets to configure one portlet from another one.
    Our first 'real life' portlets use this modality feature to display a list of documents in a document library database. A click on a list entry displays detail information of the entry in a separate dialog / mobile view including the full document richtext with inline images and attachments (leveraging a nice hidden url syntax of Domino).
    Both the list and detail portlet use Dojo's powerful Django template language to generate the HTML content.

    Backend


    The portal and portlet code and data are stored in a Lotus Domino 8.5.3 server.

    As you might know, Lotus Domino 8.5.3 contains Dojo version 1.6.x as part of the XPages runtime environment. That version is already pretty outdated and not sufficient for our portal framework usecase (e.g. no dojox.mobile.ScrollablePane).
    There is some hope that 8.5.4 will contain Dojo 1.8, but we could not wait until Q1/2013 for the 8.5.4 release (the world out there does not wait for IBM dev).
    Replacing the existing Dojo version of the server with a newer one is more or less a hack and not supported as well, so we decided to render the whole portal UI in our own code and develop a set of REST services to communicate between frontend and backend code.

    Content rendering is done in an XAgent style XPage. The page loads one or more files from the database design (added in the Java perspective of Domino Designer), parses placeholders in the text content with a regular expression (e.g. to include other files or insert paths and multilingual text phrases coming from property files in the database design) and sends the output to the browser.

    We've got REST services to load/store information about all the portlets on the portal page. Each portlet has a state and appearance information as a JSON object (the latter e.g. to store in which column of the portal a portlet should be displayed).
    The REST services are implemented with a Java control similar to IBM's rest service in the Extension Library (using the XPages extensibility API), but we removed the SSJS functionality from the control and directly call Java code (that's much cleaner than writing SSJS code that then calls our Java classes).

    For each portlet instance, we create a Notes document in the database. There is a document to store the overall state of the portal page itself and there are configuration documents in the database to configure which portlets can be added to the page by the current user.
    By replacing the backend storage code, portlet data can optionally be stored temporarily in the user session (sessionScope, viewScope). And since the whole framework is developed in a modular way, the portal UI can also be replaced by something else, e.g. to display new tabs instead of portlets. Actually, we don't use the terms "portal" and "portlet" in the framework. They are called "WorkbenchPage" and "ViewPart", similar to Eclipse terminology and displaying them in a portal layout is just a single use case.  

    Summary

    Developing this application was really fun, especially the iPad and CSS animation part.
    The only thing that was a real PITA was to get the whole stuff working on Internet Explorer 7 (fortunately, only the desktop part, not iPad mode).

    Hopefully, some of the concepts and links in this article are helpful for others as well, although there is not a single line of code in it. :-)

    New address of Mindoo GmbH in Karlsruhe, Germany

    Karsten Lehmann  30 June 2012 14:14:23
    Please note that we have moved.
    As of 1st of July 2012, our new company address in Karlsruhe, Germany is

    Mindoo GmbH

    Haid-und-Neu-Straße 7

    76131 Karlsruhe

    Germany