A Django site.
August 31, 2009
» GSoC 2008: Progress report

I have made a lot of progress on my Google Summer of Code Project.

First, I've completed the code for the generation of the directory structure for the forms. This is done using the form id, which is the name with all illegal characters removed. The only valid characters left are alpha-numeric and periods. Let me tell you, the regular expression to replace all of those characters was hell to write. But, thanks to the help of Jason Davis, I was able to make it much more readable and easier digest. You've gotta see it to get an idea of how bad it was, so here goes:


/**
* Removes all illegal characters, then removes all spaces and makes it all lower-case.
*
* @param str the form name
* @return a viable form id.
*/
public static String formNameToFormId(String str) {
return str.replaceAll("[\\$\\(\\)%`~!@#&;:\\^=\\+\\?
\"\\*\\\\////\\\\{\\}'\\]\\[\\|\\s+]", "").toLowerCase();

}

That is pretty much hell on earth to read, and it was hell on earth to type. Here's the *MUCH* simpler version:


/**
* Removes all illegal characters, then removes all spaces and makes it all lower-case.
*
* @param str the form name
* @return a viable form id.
*/
public static String formNameToFormId(String str) {
return str.replaceAll("[^a-zA-Z0-9.]", "").toLowerCase();

}

Note, you can clearly see that i'm negating that entire sequence. It does the SAME thing, but is MUCH more readable. That reads: "replace everything that isn't a letter from A to Z *OR* a to z(lower case letters) *OR* from 0 through 9 *OR* a period. Whereas the above just excluded the invalid characters explicitly but was a MONSTER of a regular expression.

Secondly, metadata persistence is written in, I'm using xstream. It's a great tool for XML serialization.

That's it. Oh, by the way, these are the things I earmarked TWO WEEKS FOR! Now, I push domain class model processing up, will work on that tomorrow.

August 28, 2009
» GSOC 2008: Week 5

So, it's time for an update. This week got off to a sketchy start, but it's gained momentum. Let me enumerate what I have done thus far with the project. First, I've added AJAX using jquery. I figured I would use jquery mainly because it provided painless AJAX goodness. Prior to even thinking of using jquery, I wrote my own AJAX code using a tutorial that I found while googling. I had to tweak it a bit, but for the most part, it seemed very standard. However, after I wrote the AJAX equivalent code, it didn't feel too elegant. So, first I'm going to show the AJAX code I wrote; then I'll show you the jquery version; finally, I'm going to show you the servlet which handles the AJAX on the server-side.

First, the AJAX I wrote:


function AjaxValidation(url, callback) {
var req = init()
req.onreadystatechange = processRequest

function init() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest()
} else if (window.ActiveXObject) {
return new ActiveXObject("Mircosoft.XMLHTTP")
}
}

function processRequest() {
if (req.readyState == 4) {
if (req.status == 200) {
if (callback) {
chkSyntaxCallBack(req.responseXML)
}
}
}
}

this.doGet = function() {
req.open("GET", url, true)
req.send(null)
}
}
function chkSyntaxCallBack(responseXML) {
var res = responseXML.getElementsByTagName("result")[0].firstChild.nodeValue
document.getElementById("out").innerHTML = res
}

function checkSyntax() {
var target = document.getElementById("groovyModel")
var url = "${pageContext.request.contextPath}/moduleServlet/groovyforms/createGroovyForm?groovyModel=" + escape(target.value)
var ajax = new AjaxValidation(url, chkSyntaxCallBack)
ajax.doGet()
}

I warned you that it wasn't too elegant. Understanding this isn't too hard. Here is the call sequence: init() -> doGet() -> processRequest() -> chkSyntaxCallBack(). Not that bad. Now let's see the jquery version.

$(window).ready(function () {
$("#groovyModel").bind("blur", function () {
$.ajax({
type: 'POST',
data: { groovyModel: $("#groovyModel").val() } ,
url: "${pageContext.request.contextPath}/moduleServlet/groovyforms/createGroovyForm" ,
cache: false ,
success: function(data) {
var res = $(data).find("result").text()
$("#out").html(res)

}
})
});
})

Okay, that's much better. A few things are still happening here. When the window is finished loading, I bind my textarea element which has the CSS id of "groovyModel" to the blur event (lost focus). Then you see the AJAX. Now this is very straight forward. We're using the POST method, we're sending whatever value is inside of the textarea at the time the event is fired, we're posting to a servlet, not going to cache, and when we're done, it's printed to the screen. Very straight forward.

Now, like I said, this is all backed on the server-side by a servlet, which is written in Groovy. So here we go:

/**
* The contents of this file are subject to the OpenMRS Public License
* Version 1.0 (the "License") you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://license.openmrs.org
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* Copyright (C) OpenMRS, LLC. All Rights Reserved.
*/
package org.openmrs.module.groovyforms.web

import javax.servlet.ServletException
import javax.servlet.http.HttpServlet
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import org.apache.commons.logging.LogFactory
import org.codehaus.groovy.control.CompilationFailedException

class CreateGroovyFormServlet extends HttpServlet {
def classLoader
static final def log = LogFactory.getLog(CreateGroovyFormServlet.class)
private static final long serialVersionUID = 066373513262051L

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
def generateTemplate = request.getParameter("template")
def generateController = request.getParameter("controller")
def finalMarkup = request.getParameter("markup")
def clazz = URLDecoder.decode(request.getParameter("groovyModel"))
def name = request.getParameter("formName")
def version = request.getParameter("version")
def res = this.checkSyntax(clazz)
if (clazz) {
if (checkSyntax(clazz)) {
response.contentType = "text/xml"
response.setHeader "Cache-Conrol", "no-cache"
response.writer.write "\n\t$res\n"
} else {
response.contentType = "text/xml"
response.setHeader "Cache-Control", "no-cache"
response.writer.write "true"
}
} else {
response.contentType = "text/xml"
response.setHeader "Cache-Control", "no-cache"
response.writer.write "\n\tPlease fill in the Form Model\n"
}
}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response)
}

@Override
void init() throws ServletException {
if (log.infoEnabled)
log.info("Initializing...")
classLoader = getClassLoader()
}

def getClassLoader() {
def gcl = new GroovyClassLoader(this.getClass().getClassLoader())
gcl
}

/**
* This method is used to relay errors to the user
* @param clazz the class
* @return the exception message or null if it was successful
*/
def checkSyntax(clazz) {
def sb = new StringBuilder()
sb << "import org.openmrs.*\n\n\n"
sb << clazz
def res = null
try {
getClassLoader().parseClass(sb.toString())

} catch (CompilationFailedException e) {
res = "Exception: ${e.message}"
}
res

}

/**
* Check if it is result groovy code.
* @param clazz the class
* @return whether or not it is result groovy code
*/
def isValidGroovy(clazz) {
def sb = new StringBuilder()
sb << "import org.openmrs.*\n\n\n"
sb << clazz
try {
getClassLoader().parseClass(sb.toString())
} catch (CompilationFailedException e) {
return false
}
return true
}
}


This servlet contains a lot of utility methods. One compiles, one initializes/returns the GroovyClassLoader, and of course doGet(), doPost() and init(). doGet() and doPost() both do the same thing, with doPost() simply delegating to doGet(). The code should be reasonably easy to understand. checkSyntax() returns null if it was parsed cleanly, otherwise it returns the exception message, the stack track wouldn't be useful in my case. It returns an XML tag <result> with "true" if it was successful, the exception message if it was not, and a message stating that the field must be filled in if it's empty or just not passed in. I implicitly import org.openmrs.* to allow for easier access to the OpenMRS domain model classes. The parent classloader for the GroovyClassLoader is set the servlet container's classloader. This gives me access to the classpath when loading Groovy classes.

I still have a bit to do, templating needs to be written in, for the most part it's done. Just have a few problems I'm facing, but I'll get through it. Too many people are relying on me succeeding. I feel that this project could help a lot of people, so i feel pressure to succeed. I still need to write in the "Edit" functionality of the "Manage Groovy Forms" page.

I'm definately making progress. More updates to come, that's for sure.

» GSoC 2008: Week 1

Okay, week 1 is nearing an end.I accomplished alot of things.

First, I completed the meta-data storage code. I wrote some tests, which revealed some bugs that needed fixing, and they were. I am happy to say, that the metadata storage works!

Writing the test was difficult due to the fact I store the forms in the system using a static List in my container class. I wound up updating to JUnit 4 to get at the @BeforeClass annotation so that the only one set of forms gets loaded into the container class. This helped me greatly. Additionally, the @AfterClass annotation was handy for cleaning up from the tests.

Secondly, Writing the code for interrogating the model was a piece of cake, thanks to groovy. All code for interrogating the model has 50 lines! That includes a model class I wrote to hold the field types and field names. I'll show you, but before I do, I should note that return statements are optional, and the final statement will be returned.


package org.openmrs.module.groovyforms.util

import java.lang.reflect.Field
import org.openmrs.module.groovyforms.metadata.model.GroovyFormsDomainModel

/*
* Utility class containing methods for class interrogation.
*/
class GroovyFormsClassUtil {
/**
* Interrogates the class for all declared fields and
* stores the type and name in a container class
* @param the {@link Class#getCanonicalName() canonical name of the class}
* @see Class#getCanonicalName()
* @return a reference to a container class containing the type
*/
static def getModel(fields) {
def domainModel = new GroovyFormsDomainModel()
def names = domainModel.fieldNames.&add;
def types = domainModel.fieldTypes.&add;
def f = fields.each {Field field ->
names field.name
types field.type.canonicalName
}
domainModel
}
}

With groovy, it's so easy and concise (as you can see). Let's explain what's going on. First, I pass in the Field array I get from Field.getDeclaredFields(). Now, groovy adds methods onto the standard JDK classes, one of those methods is a method named each() which takes a closure. Now, back to the point, I pass a Field into the closure. That closure is executed for each element (in this case, a Field). Then I add the name, and the type to a List stored in a container class, now that container class is also written in groovy!


package org.openmrs.module.groovyforms.metadata.model

/*
* Ths class holds information about the properties of the model.
*/
class GroovyFormsDomainModel {

/**
* The field names
*/
def fieldNames = []

/**
* The field types
*/
def fieldTypes = []

}

Now, the fields are Lists, not arrays. Anyways, I've gotten off on a tangent here, so let me get back on track.

What I accomplished:


Week 1:

Code to generate the directory structure, serialization of metadata back/forth between XML and POJOs (Plain Old Java Objects), Wrote tests to ensure everything works in that regard. Additionally, I wrote the code to interrogate the domain model which I will generate the forms from.

Next Week

Write up the templates for the view/controller and write code to do the generation of the view/controller. Write some tests to ensure everything generates correctly.

Unrelated to that, my stored-value card from google came today. It feels nice to be $500 richer! This is going to be the best summer, I'm already having fun doing this. It's amazing seeing the whole project evolve into something amazing.

August 3, 2009
» The overdue progress report

Apologies for not blogging as much as I should. I've focused on getting what needed to be done, done.

Tasks that have been completed thus far with a target milestone of using a mock Form, enable persistence of Questions and Values to the database:

  • Mock out a report form using the domain classes
  • Using the mocked up schema, generate a simple report form
  • Design the SQL Schema (for just FacilityDataValue and FacilityDataQuestion)
  • Write Hibernate Mapping files (for just FacilityDataValue and FacilityDataQuestion)
  • Write Data Access layer (for FacilityDataValue and FacilityDataQuestion)
  • Write Service layer (for FacilityDataValue and FacilityDataQuestion)
  • Refactor the rendering logic to use the JSP and write EL function(s) to check types using instanceof.
  • Allow a simple the mocked form from Week 1 to save the question answers.


Tasks that are are in progress, soon to be finished with a target milestone of removing the code used to mock up everything from the first few weeks; ability to use the saved schemas for rendering the report form:

  • Design SQL Schema for the rest of the domain classes
  • Write mappings for the rest of the domain classes
  • Support loading the previously saved values for a form/startdate/enddate/location into a page for viewing or editing
  • Write methods to save the rest of the domain classes in the data access layer
  • Write methods to save the rest of the domain in the service layer

Now that I have summarized work completed and in progress, let's explain the overall design:

  1. FacilityDataFormSchema serves as the overall representation of the report form in the system.
  2. FacilityDataFormSection is simply that, sections on the form, e.g., monitoring equipment status, stock status of vaccinations, number of people vaccinated, etc.
  3. FacilityDataFormQuestion holds metadata regarding a question.
  4. FacilityDataQuestion is the question itself; it specifies the datatype; it is subclassed for each question datatype; if not subclassed, then the question is considered to be "freetext" -- in other words: just simply a text-based question.
    1. CodedQuestion is a question that has a coded answer. This too is subclassed for each coded question datatype.
      1. StockQuestion is exactly as the name says, to track stock of items such as vaccinations. The coded answers are: "not_stocked_out","stocked_out","expired","not_applicable"
      2. BooleanCodedQuestion is a simple "yes","no","not applicable" type of thing; e.g., "Was there mobile clinic today?"
    2. NumericQuestion is a question which has a numeric answer, e.g., "Number of Adults Vaccinated."
  5. FacilityDataValue is what holds the values entered in the report forms for each question.
  6. FacilityDataReportFormData is a non-persisted class used for retrieving the answers for a specific report for a specified period.

Hopefully this makes up for my lack of updates.

July 14, 2009
» In the middle of GSoC: The Summer of Sync


It’s been quite a while, truth be told, quite some time without posting updates to this blog.
My GSoC project have been going through a mix being busy, many events happening, trying to find love for new things but I am sure my project is still interesting .
Today I want just to mention the status of my GSoC project ; it’s in the mid-term evaluation and we expect Google paying for all ‘well-going’ projects .

I have a detailed wiki page about my project : OpenMRS Sync, Create a new Sync Node .Everyone is welcome to leave useful comments on this project. The source code is available under the terms of OpenMRS Public Licence and can be browsed from OpenMRS website as well as they can be ‘checked-out’(downloaded) via subversion with the command

svn checkout http://svn.openmrs.org/openmrs/branches/data-synchronization-admin-ui

Other things actually are happening in my coding environment:I have been undergoing series of switching Operating Systems between Windows, Linux and Solaris and I have some insight about what does things in the best way; I have been trying to find love for Groovy/Grails at the same time I am trying the Qt toolkit .Groovy seems to be a powerfull Jav-based agile framework, easy to configure and deploy. Touching it is like drinking milk from your grandma, and when you get satisfied you feel and find that you have more energy . Qt is a cross-platform desktop GUI framework using C++and acquired by Nokia last year, Qt is the parent of KDE Desktop environment for Linux.Trying Qt is like eating bread from your granddad tagged with honey inside; you get stronger and healthier. The reality behind things like these(trying new things) should be :”Always, there is a place where you want to go, there is a person who you want to be with, there is something you always want to see happenning, there a level you always wish to be on, the is a goal you always want to achieve ! The list doesn’t end and when you feel you don’t have neither one of these desire, you don’t enjoy your life .” .

I the past 40 days, I have seen many other things happening around us and among them as I am writing: ATRACO club from Rwanda won the East-African soccer cup(CECAFA), Rwanda has named other new born gorillas (Kwitizina<www.kwitizina.org>: this is really a new brand for the Gorilla tourism in Rwanda, and it demonstrates the efforts deployed in both tourism promotion and conservation ) , Rwanda has celebrated 15 years after National Liberation and the end of Tutsis Jenocide <www.rwanda15.org>. An other event was the departurte of MJJ , the King of Pop, to the land of peace, and I can’t say anything more personal than “In paradisum deducant te Angeli; in tuo adventu suscipiant te martyres, et perducant te in civitatem sanctam Ierusalem. Chorus angelorum te suscipiat, et cum Lazaro quondam paupere æternam habeas requiem.“.

This is not the end, please stay turned to my blog and I love to hear your comments .

svn checkout http://svn.openmrs.org/openmrs/branches/data-synchronization-admin-ui

June 1, 2009
» Last week OpenMRS GSoC activities


Last week was really busy forme, I was packaging everything for traveling back to Kigali . At least now, I get time to write a couple of blog posts . I am seating in WA Dulles airport,  waiting for the plane that take me to Brussels; I am waiting  almost for the whole day and I can now summarize what I’ve done on OpenMRS during last week .

My GSoC project is again about Sync: “Data synchronization is a new OpenMRS feature allowing synchronization of data amongst a set of loosely networked servers. Such ability to exchange data is essential for operation of EMR system in rural areas where connectivity amongst sites maybe unreliable yet the need for timely centralized collection and analysis of data from remote sites exists.”

The synchronization feature was designed in a parent-child hierarchical model, to allow data to flow from from remote sites to more central parent and vice versa . A parent OpenMRS server can have many child servers and child can have only one parent . At synchronization, a child server send new change-sets to its model (SYNC REQUEST) and receives all new change-sets from the server (SYNC RESPONSE) . This can be done either via the web or with a disk drive (file) that can be carried over the parent while the child is off-line and  the parent will also issue an off-line sync response to carry back to the child . The sync via file doesn’t mean that you have to move physically to the place where the the parent server is installed ; OpenMRS is a web app and any where you can access the parent app you can sync your off-line child !

There are two additional changes on the OpenMRS data model that synchronization does :

1) synchronization_* tables are added to the data model for storing sync settings & configuration and they also store the temporary sync import/export records .

2) GUID indexes : to ensure data exchange between different OpenMRS systems, the ID fields are not enough to identify a record because there are from different MySql installations, so the GUID index columns are added to all data that can be sync-ed .

My actual project’s main aim is to provide an automated way of creating a new sync node(i.e.child) and provide the appropriate user interface . This was done in manual process and required much administrative knowledge. When creating a new sync node(i.e.child) you had to:

  • register newly created child with parent
  • back up parent server DB and move the backup to the new child server
  • restore parent’s DB
  • assign new server sync ID
  • change any server identifying information from parent to child (i.e. form entry server URL)
  • test sync connection between parent and child and finally establish periodic sync schedule

In order to achieve these project I have to start from the actual sync code . As last year I was commiting to the synchronization-admin-ui branch, I think I am going to commit to it even for this project . So I started by resolving few issues in the actual code and I merged the synchronization_bidirectional branch to the sync-admin-ui branch so that all sync changes after last GSoC be available to the sync-admin-ui . This was not a simple task because the sync_bidirectional branch also merges from trunk .

I just used Subclipse merge feature and I let both the old and new versions of code be there then I removed the old revisions where it was necessary and, sometimes I had to use a piece of regex in order to be fast .

For example, Subclipse should form somewhere two blocks of code with different versions and limit them with

<<<<<<< .working
//Code
=======
// Code
>>>>>>> .merge-right.r7385

Then in that case I used the following regex and replaced it with an empty space in order to keep the merger-right version of the code

(<<<<<<< \.working([\x00-\xFE]*?)=======)|(>>>>>>> \.merge-right\.r7385)

This is how I merged and it’s working perfectly .

My next step now is to find out a way of cloning the MySql database from the parent DB and apply it to the new child instllation automatically . If you have more ideas about how to achieve this, please leave your comments .

April 30, 2009
» Accepted into Google Summer of Code 2009

For the second year in a row, I was accepted into Google Summer of Code!!! I will be working with OpenMRS on the Facility Data Module.

My project will focus on developing tools for collecting and ultimately generating reports for aggregate data from external sources.

March 29, 2009
» On Starting the Modern Compiler

Today (10/09/2008) I ran into a weird bug with Eclipse 3.3.x. The other day, I had to remove my internal hard-drive because I was sending the laptop in for repair. I used an external exclosure to turn the internal hard-drive into an external hard-drive and plugged it into the USB port on my personal laptop. The external drive came up as the E:\ drive. Anyway, long-story-short I was abe to use my Eclipse workspace on the external drive with my personal laptop for a few days. I had to switch some Eclipse settings (i.e. ANT HOME) in order to compile code. When I switched back to using the drive in my laptop (yesterday), I kept getting the following error message.


Buildfile: C:\Documents and Settings\Justin Miranda\My Documents\My Workspace\LogicRestModule\build.xml
init:
compile-module:
[javac] Compiling 5 source files to C:\Documents and Settings\Justin Miranda\My Documents\My Workspace\LogicRestModule\build

BUILD FAILED
C:\Documents and Settings\Justin Miranda\My Documents\My Workspace\LogicRestModule\build.xml:42: Error starting modern compiler

After playing around with the Java Compiler and Ant settings and restarting Eclipse a few times, nothing seemed to be helping. I downloaded the latest version of the code I was trying to compile and was able to successfully compile that code. But on restart, an attempt to "ant clean", then "ant package-module" caused the same error.

Then I remembered a nice little trick: Eclipse Clean. For some reason that worked.

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 ??

August 5, 2008
» In a world gone mad, only a lunatic is truly insane.

I've been busy with a few things during the previous couple of weeks, mainly moving to a new flat in Trondheim. I've gotten in a few hours of work here and there though, and I'm currently back to working full time.

My main task at the moment is implementing a servlet that loads an image file from the 'complex obs' system, carves it into tiles and resizes it. My first version works very well with smaller images, but fails miserably on large ones because Tomcat runs out of memory. For example, one of the images I use for testing is an X-ray image, a 8192x9976 pixel grayscale jpeg. 8192*9976*8 bits = 78 MB of memory required to store the whole decoded image. Multiply with 3 for a 24-bit colour image.

This can be reduced significantly though, using built-in functionality in Java's Image I/O API. Setting the right Image Read Parameters allows me to only decode the portion of the image that I actually need, as well as applying subsampling to the image decoder. Seeing as each http request returns an image tile of only 256x256 pixels, there is huge room for improvement.

I enjoy listening to BBC World Service radio, and todays episode of 'Digital Planet' features a brief discussion of OpenMRS and the recent Durban conference. An mp3 of the show can be downloaded from the show's home page. (WMA/RealMedia stream, or mp3 download at the podcast link)

EDIT @ 5 August: BBC apparently only keeps podcasts from the previous week, so the show is now unavailable. I've uploaded a copy here.

In other news, my mentor Mike has spoken with Summer of Code organiser Leslie Hawthorn at the aforementioned conference, and she says we are good to go ahead with using Google Maps API. Hurrah!