Open MRS 2 0 UI Framework Options Open

  • Slides: 28
Download presentation
Open. MRS 2. 0 UI Framework Options

Open. MRS 2. 0 UI Framework Options

Open. MRS 2. 0 UI Redesign As we rewrite the UI, we have a

Open. MRS 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 Easy! Fun! Beautiful! Accessible to entry-level coders Well-supported

“All” options are on the table Spring 3 Ruby on Rails (via JRuby) JQuery

“All” options are on the table Spring 3 Ruby on Rails (via JRuby) JQuery Grails Spring MVC + JQuery + roll-our-own widgets Spring MVC + JQuery + String. Template JSF + ICEFaces YUI JQuery GXT

Spring 3 � “the leading platform to build and run enterprise Java applications. Led

Spring 3 � “the leading platform to build and run enterprise Java applications. Led and sustained by Spring. Source, 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+Spring MVC+JSP+JQuery � http: //svn. openmrs. org/openmrs-contrib/uiframeworks/spring 3 mvc+jquery ◦ Thanks Harsha! �

Spring 3+Spring MVC+JSP+JQuery � http: //svn. openmrs. org/openmrs-contrib/uiframeworks/spring 3 mvc+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 @Controller public class Patient. Controller { @Request. Mapping(value="/patient", method=Request. Method. GET)

Spring 3+Spring MVC+JSP+JQuery @Controller public class Patient. Controller { @Request. Mapping(value="/patient", method=Request. Method. GET) public String view. Patient(@Request. Param("patient. Id") Patient patient, Model model) { model. add. Attribute(patient); if (patient != null) model. add. Attribute("encounters", Context. get. Encounter. Service(). get. Encounters. By. Patient(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"> <h 1>Patient Encounters</h 1> <openmrs: encounters encounter. List="${encounters}"/> </div> <%@ attribute name="encounter. List" required="true” type="java. util. List" %>. . . <script type="text/javascript"> $(document). ready(function() { $('#encounters'). data. Table(); }); </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: for. Each var="encounter" begin="0" items="${encounter. List}"> <tr class="grade. X"> <td>${encounter. Id}</td> <td>${encounter. Datetime}</td> <td>${encounter. patient. Id}</td> </tr> </c: for. Each> </tbody> </table>

Spring 3+Spring MVC+JSP+JQuery � Pros: ◦ ◦ Flexibility (never “not be able to do”

Spring 3+Spring MVC+JSP+JQuery � 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 MVC + String. Template � http: //github. com/diptanu/openmrs-beta/tree/module ◦

. . . + Spring MVC + String. Template � http: //github. com/diptanu/openmrs-beta/tree/module ◦ -Thanks Thoughtworks! � “[String. Template'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 � String. Template instead of JSP � Recommended by Thoughtworks developers � Templates allow for page layouts and reusable components

. . . + Spring MVC + String. Template layout/layout. st <html> <head> widgets/encounters.

. . . + Spring MVC + String. Template layout/layout. st <html> <head> widgets/encounters. st <title>Patient Dashboard</title> $if(encounters)$ <link rel="stylesheet" href=". . /styles/jquery. ui. all. css" type="text/css"> <script type="text/javascript" src=". . /scripts/jquery-1. 4. 2. js"></script> j. Query(function() { <script type="text/javascript" src=". . /scripts/jquery-ui-1. 8. 2. custom. min. js"></script> j. Query('#encounters'). data. Table(); <script type="text/javascript" src=". . /scripts/jquery. data. Tables. js"></script> }); </head> </script> <table id="encounters" border="1" width="500" cellspacing="0"> <body> <thead> <div class="header">$partials/header()$</div> <tr> <div class="content">$body$</div> <th>Date</th> <div class="footer">$partials/footer()$</div> <th>Location</th> </body> <th>Type</th> </html> <th>Provider</th> </tr> </thead> <tbody> patientdashboard. st $encounters: { encounter | <div id="tabs"> <tr class="grade. X"> <ul> <td>$encounter. Datetime$</td> <li><a href="#tabs-1">Patient</a></li> <td>$encounter. location. name$</td> <li><a href="#tabs-2">Encounters</a></li> <td>$encounter. Type. name$</td> $menuext: {item| <td>$encounter. provider. given. Name$</td> <li><a href="#tabs-3">$item$</a></li> </tr> }$ }$ </ul> </tbody>. . . </table> <div id="tabs-2"> $widgets/encounters(encounters=patient. encounters)$ $else$ </div> <b>There are currently no encounters</b>. . . $endif$

. . . + Spring MVC + String. Template � Pros: ◦ Strict Model-View

. . . + Spring MVC + String. Template � 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 3 + JSF + ICEFaces � � � http: //svn. openmrs. org/openmrs-contrib/ui-frameworks/jsf+icefaces/ ◦

Spring 3 + JSF + ICEFaces � � � 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, Java. Server Faces technology establishes the standard for building server-side user interfaces. With the contributions of the expert group, the Java. Server Faces APIs are being designed so that they can be leveraged by tools that will make web application development even easier ICEFaces: J 2 EE Ajax framework for developing and deploying rich enterprise applications (REAs). With ICEfaces, enterprise Java developers can easily develop rich enterprise applications in Java, not Java. Script � JSF instead of Spring MVC � Facelets instead of JSP � ◦ Component-based ◦ Server-side state, stored in the session No Javascript required! JSF automatically reloads page fragments as required

Spring 3 + JSF + ICEFaces // JSF 1. 2 with XML-defined beans public

Spring 3 + JSF + ICEFaces // JSF 1. 2 with XML-defined beans public class Table. Bean implements Page. Bean {. . . private List headers; <ice: form> private List<Encounter. DTO> body; <ice: hidden. Text binding="#{patient. init}" /> <ice: panel. Tab. Set> public List<Encounter. DTO> get. Body() {. . . return body; <ice: panel. Tab id="encounters" label="#{msgs['encounters. tab. label']}"> } <ice: panel. Grid id="panel. Grid 2" >. . . public void set. Body(List<Encounter. DTO> body) { <ice: data. Table value="#{table. body}" var="item"> this. body = body; <ice: column> } <f: facet name="header"> <ice: output. Text value="#{msgs['encounters. date. label']}"/> public List get. Headers() { </f: facet> return headers; <ice: output. Text value="#{item. date}"/> } </ice: column> <ice: column> public void set. Headers(List headers) { <f: facet name="header"> this. headers = headers; <ice: output. Text value="#{msgs['encounters. location. label']}"/> } </f: facet> } <ice: output. Text value="#{item. location}"/> </ice: column> // JSF 2. 0 with Spring-managed beans. . . </ice: data. Table> @Component("hello. World") </ice: panel. Grid> @Scope("session") </ice: panel. Tab> public class Hello. World. Bean { </ice: panel. Tab. Set> </ice: form> public String get. Message() {. . . return "Hello World!"; } public User get. Authenticated. User() { return Context. get. Authenticated. User(); } }

Spring 3 + JSF + ICEFaces � Pros: ◦ JSF is the J 2

Spring 3 + JSF + ICEFaces � Pros: ◦ JSF is the J 2 EE 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 componentbased framework?

Ruby on Rails (via JRuby) � http: //svn. openmrs. org/openmrs-contrib/uiframeworks/jruby+rails+jquery/ � “Ruby on Rails

Ruby on Rails (via JRuby) � http: //svn. openmrs. org/openmrs-contrib/uiframeworks/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 databasebacked web applications” � Thoughtworks CTO recommends Java backends with Ro. R-via-JRuby front-ends

Ruby on Rails (via JRuby) Conventions for project layout • app • controllers •

Ruby on Rails (via JRuby) Conventions for project layout • app • controllers • helpers • models • views • layouts • (controller) • (action) • config • environments • initializers • locales • public • images • javascripts • stylesheets class Patient. Controller < Application. Controller def view @patient = Context. patient. Service. get. Patient(params[: id]. to_i) if @patient. nil? puts "No patient found!" raise "No patient found!" end @encounters = Context. encounter. Service. get. Encounters. By. Patient(@patient) # record that this user viewed this patient Patient. Viewed. record_view($omrs. authenticated_user, @patient) end def find ret = []; $omrs. patient_service. get. Patients(params[: query]). each do |patient| ret << helpers. convert_to_dto(patient) end render : json => ret end <script type="text/javascript"> $(document). ready(function() { $("#tabs"). tabs(); }); </script> <h 1><%= format @patient %> - <%= format @patient_identifier %></h 1> … <div id="tabs-2"> <%= render : partial => "widgets/encounter_list", : locals => { : encounters => @encounters } %> </div>

Ruby on Rails (via JRuby) � � Active. Record and Migrations make it very

Ruby on Rails (via JRuby) � � Active. Record and Migrations make it very easy to create (application-level) database-backed functionality jruby script/generate model Patient. Viewed user_id: int patient_id: int The “domain object” doesn’t declare any properties, because those are implied by the database table This migration is automatically created class Create. Patient. Vieweds < Active. Record: : Migration def self. up create_table : patient_vieweds do |t| t. integer : user_id t. integer : patient_id t. timestamps end def self. down drop_table : patient_vieweds end class Patient. Viewed < Active. Record: : Base # get recent Patient. Vieweds 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_id, : patient_id => openmrspatient_id }) create({ : user_id => openmrsuser_id, : patient_id => openmrspatient_id }) end

Ruby on Rails (via JRuby) � Pros: ◦ It’s Fun! ◦ Excellent RAD �

Ruby on Rails (via JRuby) � Pros: ◦ It’s Fun! ◦ Excellent RAD � Mongrel + Rails seems to be better than Jetty +Spring for this � Active. Record 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?

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

Tangent: reusable components in Ro. R From views/widgets/_patient_search. html. erb. . . <script type="text/javascript"> var options = { click. Url: function(row. Num, item) { return "/patient/view/" + item. patient_id; }, icon: function(row. Num, item) { return '<img src="/images/' + (item. gender == 'M' ? 'male' : 'female') + '. png"/>'; }, … }; $(document). ready(function() { $('#<%= id %>_form'). submit(function() { $. get. JSON("<%= patient_search_opts. search_url %>", { query: $('#<%= id %>_query'). val() }, <%= id %>_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 } %>

Grails � “GRAILS: the search is over… Have your next Web 2. 0 project

Grails � “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 Ro. R in the Java world (used to be called “Groovy on Rails” until Ro. R asked them to stop) ◦ Uses Spring, Hibernate, Site. Mesh, Jetty, … � Groovy ◦ Java + dynamic typing + closures + compiler magic ◦ 99. 9% of legal Java is legal Groovy def results = Context. patient. Service. get. Patients(“darius”). collect { [ pt. Id: it. patient. Id, name: it. person. Name. to. String() ] }

Grails + JQuery � http: //svn. openmrs. org/openmrs-contrib/ui-frameworks/grails-poc-dj/ class Patient. Controller { def view

Grails + JQuery � http: //svn. openmrs. org/openmrs-contrib/ui-frameworks/grails-poc-dj/ class Patient. Controller { def view = { def p = Context. patient. Service. get. Patient(params. int['patient. Id']); [ patient: p, encounters: Context. encounter. Service. get. Encounters. By. Patient(p) ] } def search. Json = { List<Patient> pats = Context. get. Patient. Service(). get. Patients(params['query']); def output = pats. collect { [ patient. Id: it. patient. Id, age: it. age, gender: it. gender, name: it. person. Name. to. String() ] } render output as JSON } } class Openmrs. Tag. Lib { static namespace = 'openmrs' def format = { attrs -> def obj = attrs['object'] f (obj != null) { if (obj instanceof User) { out << "${obj. username} (${format. Name(obj. person)})“ } else { out << "${obj} (don't know how to handle ${obj. class})" } } String ajax. Search. Helper(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“> ${create. Decorator. If. Necessary} $(document). ready(function() { $("#${id}_form"). submit(function() { … """ }

Grails + Grails-UI (YUI) � http: //svn. openmrs. org/openmrs-contrib/ui-frameworks/grails-poc/ ◦ Thanks Harsha! <gui: tab.

Grails + Grails-UI (YUI) � http: //svn. openmrs. org/openmrs-contrib/ui-frameworks/grails-poc/ ◦ Thanks Harsha! <gui: tab. View id="tab. View"> … <gui: tab id="t 1" label="Encounters"> <h 2>Patient Encounters</h 2> <gui: data. Table draggable. Columns="true“ column. Defs="[ [date: 'Date', sortable: true, resizeable: true], [location: 'Location', sortable: true, resizeable: true], [type: 'Type', sortable: true, resizeable: true], [provider: 'Provider', sortable: true, resizeable: true]]“ sorted. By='date‘ controller="patient” action="patient. Encounters“ params="[id: '${model. id}']" caption="click on a row, and it will expand" paginate="true" row. Expansion="false" rows. Per. Page="20" total. Records. Key="me. Total. Recs" /> </gui: tab> <openmrs: extension. Point point. Id="org. openmrs. patient. Dashboard. Tab" type="html"> … </openmrs: extension. Point> … </gui: tab. View> <head> <meta name="layout" content="openmrs"/> </head> <body> <h 1>Find a patient</h 1> <g: form controller=“patient” action=“find"> ID or Name: <g: text. Field name="id”/> <g: submit. Button 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…

Grails � Pros: ◦ It’s Fun! ◦ Almost no learning curve for current developers

Grails � Pros: ◦ It’s Fun! ◦ Almost no learning curve for current developers ◦ RAD almost as good as in Rails � GORM (analog of Active. Record) � 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 My. SQL database? ◦ Is Spring Roo worth looking into as an alternative?

GXT (EXT on GWT) � � � http: //svn. openmrs. org/openmrs-contrib/ui-frameworks/spring 3+gxt/ -Thanks Sy!

GXT (EXT on GWT) � � � http: //svn. openmrs. org/openmrs-contrib/ui-frameworks/spring 3+gxt/ -Thanks Sy! GWT is a development toolkit for building and optimizing complex browser-based applications Ext JS is a cross-browser Java. Script 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) Client UI code looks like a Swing app public class

GXT (EXT on GWT) Client UI code looks like a Swing app public class Main. Content extends Layout. Container { … public void open. Patient. Dashboard(Patient. DTO patient) { remove. All(); add(new Patient. Dashboard(patient)); layout(); } } public class Patient. Dashboard extends Layout. Container { … @Override protected void on. Render(Element parent, int pos) { super. on. Render(parent, pos); set. Layout(new Row. Layout(Orientation. VERTICAL)); … Tab. Item demographics = new Tab. Item("Demographics"); demographics. add(create. Demographics. Form()); Tab. Item encounters = new Tab. Item("Encounters"); encounters. add(new Patient. Encounter. Panel 2(patient)); tabs = new Tab. Panel(); tabs. add(demographics); tabs. add(encounters); … } You need lots of (formulaic) classes: (client) interface Patient. Service. Async (common) class Patient. DTO (server) class Patient. Service. Impl DTOs, for sending data between client and server package web. openmrs. common; … public class Encounter. DTO implements Serializable { private Integer id; private Date date; private String location; private String type; private String provider; private List<Obs. DTO> obs; … }

GXT (EXT on GWT) � This would be a completely different sort of application

GXT (EXT on GWT) � This would be a completely different sort of application

GXT (EXT on GWT) � Pros: ◦ No HTML or Javascript! ◦ “Since this

GXT (EXT on GWT) � Pros: ◦ No HTML or Javascript! ◦ “Since this is Java, the API and Java. Docs 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?

Spring 3 -JSP Spring 3 String. Template Spring 3 -JSFICEFaces JRuby-Rails Grails GXT Learning

Spring 3 -JSP Spring 3 String. Template Spring 3 -JSFICEFaces JRuby-Rails Grails GXT Learning curve (new dev) Medium High Low High? Learning curve (existing dev) None Low High Low Trivial High? Open. MRS Modules Easy ? ? ? Easy ? ? ? Embedded webserver Good (Jetty) OK (Jetty) Excellent (Mongrel) Good (Jetty) Good Embedded DB in dev mode ? ? ? ? ? ? RAD domain objects ? ? ? (Hibernate annotations? ) ? ? ? Yes (Active. Record) Yes (GORM) No? Layouts Need to integrate something Yes N/A? Yes (Sitemesh) N/A Built-in Widgets None ICEFaces Won’t use EXT Tech for widgets Roll our own String. Template JSF Partials, helpers Taglibs, templates “tight and extensible” Eclipse integration Good ? ? ? (Rad. Rails) Mediocre (Spring plugin) Excellent (Google plugin) Community Large* Small Concerns No Services Modules may be impossible

Still To Investigate � More review of JSF 2 + ICEFaces 2 � Is

Still To Investigate � 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 Active. Record/GORM in Spring-3 -based technologies? � JSR-286 Portlets