openmrs 2.0 ui framework options

28
OpenMRS 2.0 UI Framework Options

Upload: hogan

Post on 23-Feb-2016

73 views

Category:

Documents


0 download

DESCRIPTION

OpenMRS 2.0 UI Framework Options. OpenMRS 2.0 UI Redesign. As we rewrite the UI, we have a one-time opportunity to pick a new web framework All options are on the table Criteria Plug in our existing Java API Support extensions via modules Allow us to build reusable components - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: OpenMRS  2.0 UI Framework Options

OpenMRS 2.0 UI Framework Options

Page 2: OpenMRS  2.0 UI Framework Options

As we rewrite the UI, we have a one-time opportunity to pick a new web framework

All options are on the table Criteria

Plug in our existing Java API Support extensions via modules Allow us to build reusable components Easy! Fun! Beautiful! Accessible to entry-level coders Well-supported

OpenMRS 2.0 UI Redesign

Page 3: OpenMRS  2.0 UI Framework Options

Spring 3 Spring MVC + JQuery + roll-our-own widgets Spring MVC + JQuery + StringTemplate JSF + ICEFaces

Ruby on Rails (via JRuby) JQuery

Grails YUI JQuery

GXT

“All” options are on the table

Page 4: OpenMRS  2.0 UI Framework Options

“the leading platform to build and run enterprise Java applications. Led and sustained by SpringSource, Spring delivers significant benefits for many projects, increasing development productivity and runtime performance while improving test coverage and application quality”

Why?◦ We use it, we like it◦ Extremely flexible◦ Heavily used, and getting better all the time

Spring 3

Page 5: OpenMRS  2.0 UI Framework Options

http://svn.openmrs.org/openmrs-contrib/ui-frameworks/spring3mvc+jquery◦ Thanks Harsha!

The modern version of what we're doing now

Maven! Jetty! Spring 3 MVC has lots of timesaving

features we haven’t taken advantage of Jquery is powerful and we haven't taken

advantage Consciously build reusable UI widgets

Spring 3+Spring MVC+JSP+JQuery

Page 6: OpenMRS  2.0 UI Framework Options

Spring 3+Spring MVC+JSP+JQuery

@Controllerpublic class PatientController {

@RequestMapping(value="/patient", method=RequestMethod.GET)public String viewPatient(@RequestParam("patientId") Patient patient,

Model model) {model.addAttribute(patient);if (patient != null)model.addAttribute("encounters",

Context.getEncounterService().getEncountersByPatient(patient));}

}<script type="text/javascript">

$(function() {$('#tabs').tabs();

});</script>

...

<div id="tabs"><ul>

<li><a href="#tabs-1">Patient</a></li>

<li><a href="#tabs-2">Encounters</a></li>

</ul>...<div id="tabs-2">

<h1>Patient Encounters</h1><openmrs:encounters

encounterList="${encounters}"/>

</div>

<%@ attribute name="encounterList" required="true” type="java.util.List" %>...<script type="text/javascript">

$(document).ready(function() {$('#encounters').dataTable();

});</script>

<table id="encounters" border="1" width="500" cellspacing="0"><thead>

<tr><th>Encounter Id</th><th>Encounter Date</th><th>Encounter Patient</th></tr>

</thead><tbody>

<c:forEach var="encounter" begin="0" items="${encounterList}">

<tr class="gradeX"><td>${encounter.encounterId}</td><td>${encounter.encounterDatetime}</

td><td>${encounter.patientId}</td></tr>

</c:forEach></tbody></table>

Page 7: OpenMRS  2.0 UI Framework Options

Pros:◦ Flexibility (never “not be able to do” something)◦ Strong community◦ Configured right, it allows RAD◦ No learning curve for existing devs

Cons:◦ Significant learning curve for newbies (lots of

technologies, and they are not simplified)

Open questions:◦ Can we leverage Hibernate annotations?

Spring 3+Spring MVC+JSP+JQuery

Page 8: OpenMRS  2.0 UI Framework Options

http://github.com/diptanu/openmrs-beta/tree/module ◦ -Thanks Thoughtworks!

“[StringTemplate's] distinguishing characteristic is that it strictly enforces model-view separation unlike other engines. Strict separation makes websites and code generators more flexible and maintainable.”

Controllers like you're used StringTemplate instead of JSP Recommended by Thoughtworks developers Templates allow for page layouts and

reusable components

... + Spring MVC + StringTemplate

Page 9: OpenMRS  2.0 UI Framework Options

... + Spring MVC + StringTemplate

layout/layout.st<html><head> <title>Patient Dashboard</title> <link rel="stylesheet" href="../../../styles/jquery.ui.all.css" type="text/css"> <script type="text/javascript" src="../../../scripts/jquery-1.4.2.js"></script> <script type="text/javascript" src="../../../scripts/jquery-ui-1.8.2.custom.min.js"></script> <script type="text/javascript" src="../../../scripts/jquery.dataTables.js"></script></head>

<body><div class="header">$partials/header()$</div><div class="content">$body$</div><div class="footer">$partials/footer()$</div>

</body></html>

patientdashboard.st<div id="tabs"> <ul> <li><a href="#tabs-1">Patient</a></li> <li><a href="#tabs-2">Encounters</a></li> $menuext:{item| <li><a href="#tabs-3">$item$</a></li> }$ </ul> ... <div id="tabs-2"> $widgets/encounters(encounters=patient.encounters)$ </div>...

widgets/encounters.st$if(encounters)$<script type="text/javascript">

jQuery(function() {

jQuery('#encounters').dataTable();});

</script><table id="encounters" border="1" width="500" cellspacing="0"><thead>

<tr><th>Date</th><th>Location</th><th>Type</th><th>Provider</th></tr>

</thead><tbody>$encounters:{ encounter |

<tr class="gradeX"><td>$encounter.encounterDatetime$</td><td>$encounter.location.name$</td><td>$encounter.encounterType.name$</

td><td>$encounter.provider.givenName$</

td></tr>

}$</tbody></table>

$else$ <b>There are currently no encounters</b> $endif$

Page 10: OpenMRS  2.0 UI Framework Options

Pros:◦ Strict Model-View separation will make us program

better

Cons:◦ Unclear whether this approach has significant

advantages over more-commonly-used technologies

Open Questions:◦ When a module attaches to an extension point, that

now becomes view-only, and can’t add any data to the model. Right?

... + Spring MVC + StringTemplate

Page 11: OpenMRS  2.0 UI Framework Options

http://svn.openmrs.org/openmrs-contrib/ui-frameworks/jsf+icefaces/◦ Thanks Shazin! (ICEFaces 1.8, JSF 1.2, no Spring)◦ JSF 2 with Spring in progress

JSF: Developed through the Java Community Process under JSR - 314, JavaServer Faces technology establishes the standard for building server-side user interfaces. With the contributions of the expert group, the JavaServer Faces APIs are being designed so that they can be leveraged by tools that will make web application development even easier

ICEFaces: J2EE Ajax framework for developing and deploying rich enterprise applications (REAs). With ICEfaces, enterprise Java developers can easily develop rich enterprise applications in Java, not JavaScript

JSF instead of Spring MVC◦ Component-based◦ Server-side state, stored in the session

Facelets instead of JSP

No Javascript required! JSF automatically reloads page fragments as required

Spring 3 + JSF + ICEFaces

Page 12: OpenMRS  2.0 UI Framework Options

Spring 3 + JSF + ICEFaces...<ice:form>

<ice:hiddenText binding="#{patient.init}" /><ice:panelTabSet>

...<ice:panelTab id="encounters"

label="#{msgs['encounters.tab.label']}"><ice:panelGrid id="panelGrid2"

>...<ice:dataTable

value="#{table.body}" var="item">

<ice:column>

<f:facet name="header">

<ice:outputText value="#{msgs['encounters.date.label']}"/>

</f:facet>

<ice:outputText value="#{item.date}"/></

ice:column>

<ice:column>

<f:facet name="header">

<ice:outputText value="#{msgs['encounters.location.label']}"/>

</f:facet>

<ice:outputText value="#{item.location}"/></

ice:column>...

</ice:dataTable></ice:panelGrid>

</ice:panelTab> </ice:panelTabSet>

</ice:form>...

// JSF 1.2 with XML-defined beans

public class TableBean implements PageBean { private List headers; private List<EncounterDTO> body;

public List<EncounterDTO> getBody() { return body; }

public void setBody(List<EncounterDTO> body) { this.body = body; }

public List getHeaders() { return headers; }

public void setHeaders(List headers) { this.headers = headers; } }

// JSF 2.0 with Spring-managed beans

@Component("helloWorld")@Scope("session")public class HelloWorldBean {

public String getMessage() { return "Hello World!"; }

public User getAuthenticatedUser() { return Context.getAuthenticatedUser(); }

}

Page 13: OpenMRS  2.0 UI Framework Options

Pros:◦ JSF is the J2EE 6 Standard◦ ICEFaces looks good

Cons:◦ Steep learning curve◦ Poor RAD (editing a single server-side component

requires restarting Jetty)

Open Questions:◦ How will module extensions work in a component-based

framework?

Spring 3 + JSF + ICEFaces

Page 14: OpenMRS  2.0 UI Framework Options

http://svn.openmrs.org/openmrs-contrib/ui-frameworks/jruby+rails+jquery/

“Ruby on Rails is an open-source web framework that’s optimized for programmer happiness and sustainable productivity. It lets you write beautiful code by favoring convention over configuration”

“A domain-specific-language for database-backed web applications”

Thoughtworks CTO recommends Java backends with RoR-via-JRuby front-ends

Ruby on Rails (via JRuby)

Page 15: OpenMRS  2.0 UI Framework Options

Ruby on Rails (via JRuby)class PatientController < ApplicationController def view @patient = Context.patientService.getPatient(params[:id].to_i) if @patient.nil? puts "No patient found!" raise "No patient found!" end @encounters = Context.encounterService.getEncountersByPatient(@patient) # record that this user viewed this patient PatientViewed.record_view($omrs.authenticated_user, @patient) end def find ret = []; $omrs.patient_service.getPatients(params[:query]).each do |patient| ret << helpers.convert_to_dto(patient) end render :json => ret end end

Conventions for project layout•app

• controllers• helpers• models• views

• layouts• (controller)

• (action)•config

• environments• initializers• locales

•public• images• javascripts• stylesheets

<script type="text/javascript"> $(document).ready(function() { $("#tabs").tabs(); });</script>

<h1><%= format @patient %> - <%= format @patient.patient_identifier %></h1>

… <div id="tabs-2"> <%= render :partial => "widgets/encounter_list",

:locals => { :encounters => @encounters } %> </div>

Page 16: OpenMRS  2.0 UI Framework Options

ActiveRecord and Migrations make it very easy to create (application-level) database-backed functionality

jruby script/generate model PatientViewed user_id:int patient_id:int

Ruby on Rails (via JRuby)

This migration is automatically createdclass CreatePatientVieweds < ActiveRecord::Migration def self.up create_table :patient_vieweds do |t| t.integer :user_id t.integer :patient_id

t.timestamps end end

def self.down drop_table :patient_vieweds endend

The “domain object” doesn’t declare any properties, because those are implied by the database table

class PatientViewed < ActiveRecord::Base

# get recent PatientVieweds for the given user def self.recently_viewed(user_id, n=5) all(:conditions => ["user_id = ?", user_id], :order => "created_at DESC", :limit => n ) end # get ids of patients recently viewed by the current user def self.recently_viewed_ids(user_id, n=5) recently_viewed(user_id, n).collect { |pv| pv.patient_id } end

# record that a User viewed a Patient def self.record_view(openmrsuser, openmrspatient) destroy_all({ :user_id => openmrsuser.user_id, :patient_id => openmrspatient.patient_id }) create({ :user_id => openmrsuser.user_id, :patient_id => openmrspatient.patient_id }) end end

Page 17: OpenMRS  2.0 UI Framework Options

Pros:◦ It’s Fun!◦ Excellent RAD

Mongrel + Rails seems to be better than Jetty +Spring for this ActiveRecord and migrations

Cons:◦ Rails session != Java session => need to write our own session management◦ No idea of Services

Domain objects are supposed to be intelligent Wouldn’t be able to use the full power of the framework, because of our dumb domain

objects.◦ Potential for some annoying incompatibility to hit us out of the blue

Open Questions:◦ How would module extensions work? Would an omod contain Java code *and* Ruby

code? (Yuck.)◦ How would Ruby migrations work with Liquibase?◦ How good is Eclipse integration?

Ruby on Rails (via JRuby)

Page 18: OpenMRS  2.0 UI Framework Options

Tangent: reusable components in RoRFrom views/widgets/_patient_search.html.erb...<script type="text/javascript">

var options = {clickUrl: function(rowNum, item) {

return "/patient/view/" + item.patient_id;},icon: function(rowNum, item) {

return '<img src="/images/' + (item.gender == 'M' ? 'male' : 'female') + '.png"/>';

},…

};

$(document).ready(function() {$('#<%= id %>_form').submit(function() {

$.getJSON("<%= patient_search_opts.search_url %>",

{ query: $('#<%= id %>_query').val() },

<%= id %>_results_results_callback(options)

);return false;

})});

</script>

<form id="<%= id %>_form">ID or Name: <input id="<%= id %>_query" type="text"/> <input

type="submit" id="<%= id %>_button"/></form>

<%= render :partial => "widgets/vertical_panel",:locals => { :id => "#{id}_results", :options =>

patient_search_opts } %>

Page 19: OpenMRS  2.0 UI Framework Options

“GRAILS: the search is over…Have your next Web 2.0 project done in weeks instead of months. Grails delivers a new age of Java web application productivity.”

Attempts to replicate the magic of RoR in the Java world (used to be called “Groovy on Rails” until RoR asked them to stop)◦ Uses Spring, Hibernate, SiteMesh, Jetty, …

Groovy◦ Java + dynamic typing + closures + compiler magic◦ 99.9% of legal Java is legal Groovy

Grails

def results = Context.patientService.getPatients(“darius”).collect { [ ptId: it.patientId, name: it.personName.toString() ]}

Page 20: OpenMRS  2.0 UI Framework Options

http://svn.openmrs.org/openmrs-contrib/ui-frameworks/grails-poc-dj/

Grails + JQuery

class PatientController {

def view = { def p = Context.patientService.getPatient(params.int['patientId']); [ patient: p, encounters: Context.encounterService.getEncountersByPatient(p) ] } def searchJson = { List<Patient> pats = Context.getPatientService().getPatients(params['query']); def output = pats.collect { [ patientId: it.patientId, age: it.age, gender: it.gender, name: it.personName.toString() ] } render output as JSON }}

class OpenmrsTagLib { static namespace = 'openmrs'

def format = { attrs ->def obj = attrs['object']f (obj != null) {

if (obj instanceof User) {

out << "${obj.username} (${formatName(obj.person)})“

} else {out <<

"${obj} (don't know how to handle ${obj.class})" }

}

String ajaxSearchHelper(Map attrs) {…

return """ <form id="${id}_form“>

<input id="${id}" type="text" size="40"/><input type="submit" id="${id}_button"

value="Search"/></form><div id="${id}_results" class="vertical-panel"></div><script type="text/javascript“>

${createDecoratorIfNecessary}\$(document).ready(function() { \$("#$

{id}_form").submit(function() {…

""" }

Page 21: OpenMRS  2.0 UI Framework Options

http://svn.openmrs.org/openmrs-contrib/ui-frameworks/grails-poc/◦ Thanks Harsha!

Grails + Grails-UI (YUI)

<gui:tabView id="tabView">…<gui:tab id="t1" label="Encounters">

<h2>Patient Encounters</h2><gui:dataTable

draggableColumns="true“columnDefs="[

[date:'Date', sortable:true, resizeable: true],

[location:'Location', sortable:true, resizeable: true],

[type:'Type', sortable:true, resizeable: true],

[provider:'Provider', sortable:true, resizeable: true]]“sortedBy='date‘

controller="patient”

action="patientEncounters“params="[id:'$

{model.id}']" caption="click on a row, and it will expand" paginate="true" rowExpansion="false" rowsPerPage="20" totalRecordsKey="meTotalRecs" />

</gui:tab>

<openmrs:extensionPoint

pointId="org.openmrs.patientDashboardTab" type="html">…

</openmrs:extensionPoint>…

</gui:tabView>

<head> <meta name="layout"

content="openmrs"/> </head>

<body><h1>Find a patient</h1><g:form controller=“patient”

action=“find">ID or Name:<g:textField

name="id”/><g:submitButton

name="submit" value=“Search"/></g:form>…

<body>

It’s unlikely that we’d choose YUI over Jquery, even if it does come in the Grails-UI plugin…

Page 22: OpenMRS  2.0 UI Framework Options

Pros:◦ It’s Fun!◦ Almost no learning curve for current developers◦ RAD almost as good as in Rails

GORM (analog of ActiveRecord) Jetty (needs to restart more often than Mongrel)

◦ Sitemesh handles page templates◦ Auto-recompiling groovy taglibs are great for pulling repeated code out of pages

Cons:◦ Small community, possible lack of future support◦ IDE integration is surprisingly mediocre◦ A couple times I’ve read: “we lost almost all the productivity gains Grails gave us because

of undocumented bugs in Grails”. (Personally Google searching has found me answers for all errors I’ve searched for.)

Open Questions:◦ Can we use the embedded database to allow you to develop without even having a MySQL

database?◦ Is Spring Roo worth looking into as an alternative?

Grails

Page 23: OpenMRS  2.0 UI Framework Options

http://svn.openmrs.org/openmrs-contrib/ui-frameworks/spring3+gxt/-Thanks Sy!

GWT is a development toolkit for building and optimizing complex browser-based applications

Ext JS is a cross-browser JavaScript library for building rich internet applications. Ext JS features high performance, customizable UI widgets and a well-designed and extensible component model.

=> Ext GWT is a Java library for building rich internet applications with Google Web Toolkit (GWT)

You only write Java! GWT produces HTML, Javascript, AJAX, … “Rich Internet Application” instead of “a webapp”

GXT (EXT on GWT)

Page 24: OpenMRS  2.0 UI Framework Options

GXT (EXT on GWT)

DTOs, for sending data between client and serverpackage web.openmrs.common;…public class EncounterDTO implements Serializable {

private Integer id;private Date date;private String location;private String type;private String provider;private List<ObsDTO> obs;…

}

Client UI code looks like a Swing app

public class MainContent extends LayoutContainer {…public void openPatientDashboard(PatientDTO

patient) {removeAll();add(new

PatientDashboard(patient));layout();

}}

public class PatientDashboard extends LayoutContainer {…@Overrideprotected void onRender(Element parent, int

pos) { super.onRender(parent, pos);setLayout(new

RowLayout(Orientation.VERTICAL));…TabItem demographics = new

TabItem("Demographics");demographics.add(createDemographicsForm());

TabItem encounters = new TabItem("Encounters");

encounters.add(new PatientEncounterPanel2(patient));

tabs = new TabPanel();tabs.add(demographics);tabs.add(encounters);…

}

You need lots of (formulaic) classes:

(client)interface PatientServiceinterface PatientServiceAsync

(common)class PatientDTO

(server)class PatientServiceImpl

Page 25: OpenMRS  2.0 UI Framework Options

This would be a completely different sort of application

GXT (EXT on GWT)

Page 26: OpenMRS  2.0 UI Framework Options

Pros:◦ No HTML or Javascript!◦ “Since this is Java, the API and JavaDocs are available in the IDE as context

sensitive help and the source code is available to look at instead of referring to (outdated) webpages, also has a large library of examples and showcase”

Cons:◦ Have to build a set of DTOs and services specifically for GWT◦ Steep(?) learning curve for existing and newbie devs◦ “the initial load to the client's browser is fairly large but a good framework

for loading is in place. the plus side to this is once it is loaded, the page never has to be loaded again so page loads do not exist, only data calls (reducing input/output over the wire as well as server load)”

Open Questions:◦ Could modules plug in, given the client-server architecture?

GXT (EXT on GWT)

Page 27: OpenMRS  2.0 UI Framework Options

Spring3-JSP

Spring3-StringTemplate

Spring3-JSF-ICEFaces

JRuby-Rails Grails GXT

Learning curve (new dev)

Medium Medium High Low Low High?

Learning curve (existing dev)

None Low High Low Trivial High?

OpenMRS Modules

Easy ??? ??? ??? Easy ???

Embedded webserver

Good (Jetty) Good (Jetty) OK (Jetty) Excellent (Mongrel)

Good (Jetty) Good

Embedded DB in dev mode

??? ??? ??? ??? ??? ???

RAD domain objects

??? (Hibernate annotations?)

??? (Hibernate annotations?)

??? Yes (ActiveRecord)

Yes (GORM) No?

Layouts Need to integrate something

Yes N/A? Yes Yes (Sitemesh)

N/A

Built-in Widgets

None None ICEFaces Won’t use Won’t use EXT

Tech for widgets

Roll our own StringTemplate

JSF Partials, helpers

Taglibs, templates

“tight and extensible”

Eclipse integration

Good Good ??? ??? (RadRails) Mediocre (Spring plugin)

Excellent (Google plugin)

Community Large Large* Large Large* Small Small

Concerns No Services Modules may be impossible

Page 28: OpenMRS  2.0 UI Framework Options

More review of JSF 2 + ICEFaces 2 Is GXT even an option?

◦ Can it support modules? Can we use an embedded DB in

development mode with any of these technologies?

Can Hibernate Annotations provide some of the functionality of ActiveRecord/GORM in Spring-3-based technologies?

JSR-286 Portlets

Still To Investigate