A Django site.
September 3, 2008
» A Look At Free/OpenSource Cross-Platform Installers

Software Distribution is an essential part of Software Development and can sometimes be the first impression that can make or break the user's opinion about a software. We, as software programmers forget the importance of easy distribution and easy installation of software that we develop. We do not understand the problems that a new computer user or a non-programmer may face. And I experienced this first hand about 2 weeks back, when a physician friend of mine heard that I was working on OpenMRS.

I was lucky enough to work on OpenMRS this summer and learnt a lot more about Medical Informatics during this period than I expected. Hearing this, my friend openrmswanted to install OpenMRS at his clinic which already used Tally (hehe... isn't that innovative??) for storing patient records, observations and prescriptions. He practices at Kolkata, visits different hospitals and sometimes the patients he attended at a hospital come to his clinic. When I told him that OpenMRS was a webapp, he got all excited and I narrated him all the features that OpenMRS could provide and help him manage his patients better through the web, only if he could host OpenMRS from his clinic. I'll skip the other interesting parts and his extra-terrestrial expressions ;-), since we are actually talking about software distribution.

So then came the day when I was about to leave office and he was in his clinic trying to install OpenMRS. It was Independence Day and the clinic was closed but he was excited to experience the new-age medical informatics :-)) When I first got his call he had downloaded the Windows Installer. I was pretty sure it was for an older version and hence told him to instead download the OpenMRS Appliance, which is a VM Image that can be run from one of the virtual machine softwares. Yaw Anokwa made this wonderful Virtual Image with Ubuntu + All Necessary Stuff (tomcat, mysql, demo data) and OpenMRS running. You just have to have VirtualBox or VMPlayer or VmWare Workstation and load the VMimage and wait for Ubuntu to start. It is simple, fast and safe to play with... But for novice users, I just realized it wasn't easy enough. My friend installed VirtualBox and loaded the image. It booted fine, but the network wasn't working and OpenMRS webapp could not be reached from the Windows host. After being on call for close to an hour, we just couldn't make the networking work!! I advised him to install VMPlayer instead and run the image. This time everything ran fine, but some changes had to be made in the Norton 360 Firewall. He kept complaining that Windows XP was punishably slow and then I realized that his 512Mb wasn't enough to virtualize :-( ... So we were back to where it all started!! The Windows Installer that OpenMRS distributes is based on Bitrock. He first tried the OpenMRS 1.1 Installer, but it is an older version that hasn't been upgraded for a year or so... Everything installed fine and he was happy to use it, but it didn't have the features I talked about that were added in newer releases of OpenMRS. I walked him through the manual installation and finally we managed to get OpenMRS up-and-running at 2am in the morning and he having spent about 8hrs on it. Last week when I asked him, he still wasn't using OpenMRS for his clinic and hospital. May be the first experience made him bitter!!

With that episode in my mind, I pledged him that within the next month or so I'll give him and easy to install setup and he'll be happy using OpenMRS. And that's when began my chase to find an easy to use, cross-platform installer framework. OpenMRS has lots of implementations on different platforms (Windows, Linux and Mac) and hence I wanted the installer to be cross-platform. At my office, we generally use Windows Installer or NSIS for making installers. But those are only for Windows. These 2 frameworks are so simple and extensible to use that I was thinking if there was something similar and cross-platform, I could make an OpenMRS Installer in an hour. But sadly, that wasn't the case... I tried a variety of installer frameworks, but couldn't find any of them as simple as NSIS or Windows Installer (msi). The following are the installer frameworks I tried working on:

Installer Framework Short Description Problems
1.) Antigen Antigen (Ant Installer Generator) is a tool to take an Ant build script, combine it with a GUI and wrap it up as an executable jar file. Its primary purpose is to create powerful graphical installers from Ant scripts. Couldn't get it to execute ant-calls at lots of places. Didn't work in openSuSE 11.0 due to some incomplete ant configurations. Hasn't been updated in a long time
2.) IzPack IzPack-generated installers require Java. They are simple, efficient and fast to use. Simple executable deployment is best done through IzPack. Isn't very powerful. Good for simple image deployment, but isn't highly configurable and powerful.
3.) OpenInstaller A newer cross-platform installer framework that is completely customizable and written in Java. Glassfish uses this installer framework. Not much documentation. Complex to implement and doesn't look native on all platforms
4.) Netbeans Installer (nbi) A completely customizable and powerful installer framework. Configuration Logic is written in Java and can be used to do anything and everything that Java programs can do. Old documentation. Requires some effort to get up and running with all the scripts.

So finally, I decided to work on using the Netbeans Installer. Netbeans Installer already has components like Tomcat, MySQL, Glassfish, OpenESB and their deployment scripts. And I thought it will simplify my effort... Dmitry Lipin of Sun Microsystems, the lead developer of the NBI team has been of great help over the past weeks and has helped a lot in explaining about nbi... While I was building the installer, 2 other colleagues of mine got interested in OpenMRS and have helped build some parts and want to contribute to OpenMRS code in a larger way!!

I have successfully been able to build an Installer/Uninstaller that can deploy Tomcat/Glassfish, MySQL and the OpenMRS web application on Windows, Linux, OSX, Solaris. The demo data set, JRE/JDK and starting the respective servers are yet to be completed.

Update: The OpenMRS Windows Installer based on Bitrock has been upgraded to install the latest version of OpenMRS. Is it useful for the OpenMRS community to have a cross-platform installer?? Or do the Windows guys only need an Installer ??

June 27, 2008
» Barcode Fun With OpenMRS

The Registration Module is supposed to generate Barcode images that will be printed on stickers and given to patients on their ID cards. This will help easier logo  identification of patients and quicker patient registration. For this purpose, we use the “Patient Identifier” to create barcodes. The “Patient Identifier” is hopefully unique and will help create bug-free barcodes.

Barcodes have a lot of different standards like Code39, Code128, UPC-A, UPC-E etc. Each of these standards were designed for a specific industry and application. And the Registration Module should support different standards so that it can be easy for the implementers of OpenMRS to choose any standard that they want. Thus, began my journey to find a way to generate customizable and easy barcodes.

Having just finished with the commit for the barcode generation in my registration module, I am extremely happy to say that Barcode Generation through the Registration Module is very easy. Still have to implement a print dialog box, but then the way to generate barcode is done through a pre-built servlet... I used an open-source barcode library called Barcode4j, which can generate barcodes in a variety of standards as well as image formats.

Barcode4j is an excellent library and after comparing about 15 different barcode libraries, I found it to be the most easy-to-use and extensible. Barcode4j already provides a Java Servlet which needs to be passed different parameters and it generates the barcode image “just-like-that”. The only thing I had to do was create a mapping for the servlet in the modules “config.xml”.

And that was it... Those nice black lines were shown on screen beside the patient search results. Now I needed to see if the barcodes are accurate and working. I took a printout of the image and scanned it through the barcode scanner. Hurray!! It worked!! And thus I realized that the servlet was accurately creating barcodes. Now moving onto creating a nice AJAX print dialog and probably some useful UI for creating identity cards.

June 16, 2008
» Improving Java Web Performance With C/C++

From the very first day that I had been working on OpenMRS, I felt that OpenMRS ran a little slower than I expected. Probably the old OpenMRS demo server openmrs_logo adds to the slowness. Later, when we were discussing about how Hibernate sessions should be implemented in OpenMRS and Java Web Apps in general, I was again brought to think about OpenMRS performance.

Since OpenMRS community generally implements on Tomcat, my main aim was to improve performance of the servlet container. One simple way to improve performance, which I had heard of earlier was the use of Apache has the “Apache Portable Runtime” (APR) project with Native Libraries. The APR uses native libraries with JNI to improve the server performance on a specific platform. In short, Tomcat is given some local OS steroids and currently works on Windows and POSIX-based systems.

The APR library is somewhat an irony for 2 main reasons:

  • I’ve heard this argument that Tomcat runs faster than Apache in some benchmarks. These guys argue that Java is faster than C/C++ and hence Tomcat wins.
  • On the other hand, APR and Native Tomcat uses JNI code written in C/C++ to improve performance.

Either ways, I think generalizing the above statements isn’t correct and hence I went forward to see if APR does improve performance of our web application. I used Windows Vista and Tomcat 6.0.16 for the test and Windows is probably what most OpenMRS implementations use. You can download the native binaries for Windows from here & APR from here.  Add the extracted files to Path and place the tcnative-1.dll in APR’s bin folder.

And the first thing I observed tomcat started little faster and even OpenMRS initialized slightly faster.

 
Before
After
OpenMRS initialization 192ms 183ms
Tomcat Server Startup 12892ms 11449ms

But startup improvement is not all. We want to check how good the application is performing and Apache Benchmark (ab) is a good way to test static content, but isn’t very good at dynamic content... I wanted to use Faban after I remembered Scott Oak’s writeup from last year, but couldn’t find enough time for the testing with Faban...

Instead, I used JMeter which is a nice generalized test that replicates how a user interacts with the web application. You can send POST requests with parameters and also simulate your test plan, just like a normal web user would use your application. Here are some of the results on different OpenMRS pages with 10 concurrent requests and average of 3 runs on my dual core server:

 
Without APR
With APR
OpenMRS homepage 225.7ms/request 185.7ms/request
User Login 1464.2ms/request 1185.3ms/request
Find patient 95ms/request 80ms/request
Patient dashboard 2887.6ms/request 1984.3ms/request

My first observation was that the first run on the test completely sucks. The later runs improve performance drastically. This is because of tomcat 6 has good caching mechanism and was shown with or without APR. Another thing I observed was that beyond 500 concurrent users the application was crying and tomcat was hanging up. APR or no APR didn’t matter much... I’ve yet to analyze why it wouldn’t scale any further, but must be something related to Hibernate sessions. May be some experienced developer can look into these figures, perform some more specific benchmarks and improve scalability.

June 10, 2008
» Second Week For OpenMRS Coding

Last week was really a hectic time for me and hence haven’t found much time to code or blog. A close friend Debojit is in a new Idol-like reality show called “Jo Jeeta Wohi Superstar” and we are back in the publicity campaign like we were when last time he won Saregamapa Challenge 2005. The good thing is that I’m still writing code (to game the online voting), but not exactly for OpenMRS… But I did some work on OpenMRS and nearly had a deliverable basic patient search.

The patient search on my Registration Module has taught me a few important lessons. OpenMRS’s web application uses the Model-View-Controller (MVC) through Spring Framework. I have a controller which calls some methods from the OpenMRS API and retrieves patient information. Normally, the practice is to return values from the Controller to the View (JSP here) is through the use of a bean’s getter methods. This means that the Controller sets the Bean object with the values from the database and the View (i.e. JSP) page uses to the getter methods to get values.

But instead of a bean, I tried to return a double-dimensional array and got stuck with the following... I’m still wondering why I can’t access length variable of the array. Look at the code snippets below and may be I’ll get some hints from you!!

RegistrationController:

protected String[][] formBackingObject(HttpServletRequest request) throws Exception {
        String[][] searchedPatients = new String[0][0];
        if (request.getParameter("phrase") != null) {
            List<Patient> patients = Context.getPatientService().getPatients(request.getParameter("phrase"));
            searchedPatients = new String[patients.size()][8];
            for (int i = 0; i < patients.size(); i++) {
                searchedPatients [i][0] = (patients.get(i)).getPatientIdentifier().getIdentifier();
                searchedPatients [i][1] = (patients.get(i)).getGivenName();
                searchedPatients [i][2] = (patients.get(i)).getMiddleName();
                searchedPatients [i][3] = (patients.get(i)).getFamilyName();
                searchedPatients [i][4] = String.valueOf((patients.get(i)).getAge());
                searchedPatients [i][5] = (patients.get(i)).getGender();
                searchedPatients [i][6] = (patients.get(i)).getTribe().getName();
                searchedPatients [i][7] = (patients.get(i)).getBirthdate().toString(); 
        } 
        log.info("# of patients found: "+searchedPatients.length); 
        return searchedPatients;           
        }
        return searchedPatients; 
}

This String[][] called searchedPatients can be accessed as registrationForm according to my moduleApplicationContext mapping. But in my JSP page when I try to access the .length variable of the registration form there seems to be a problem.

registrationForm.jsp

<c:forEach var="row" begin="0" end="${registrationForm.length}">
    <tr>
        <td>${registrationForm[row][0]}</td>
        <td>${registrationForm[row][1]}</td>
        <td>${registrationForm[row][2]}</td>
        <td>${registrationForm[row][3]}</td>
        <td>${registrationForm[row][4]}</td>
        <td>${registrationForm[row][5]}</td>
        <td>${registrationForm[row][6]}</td>
        <td>${registrationForm[row][7]}</td>
    </tr>
    </c:forEach>

I get an error where the JSP page throws a NumberFormatException for “length” input. Now I was baffled why it was trying to take “length” as input string, when it should have taken the length of the array as its input.

Anyways, with the Bean the current patient search is working, but I have to get back to coding quickly and build a good UI, for which I’m using jQuery. With jQuery I plan to implement AJAX and also some simple but useful UI improvements. Next in-line is searching using the barcode reader and a lot more left to do… Hopefully, I’ll do more!!