Friday, July 30, 2010

Extending GWT to support Canvas and Multi-touch

Medrium has built a document management system about 8 years ago. This allows users to scan/upload documents as well as the ability to annotate - circle, add text, markup .. the images. Our goal has always been to be platform and browser agnostic. When we built this imaging/document management system, browsers had no ability to "annotate/markup" images. We also wanted to use TIFF formats since this is the most common format used by Scanners. We started off using Java applets and some open source java code .. for reading TIFF images. We later switched to JAI - Java Advanced Imaging. The document management system has served us well, but with advances in browsers/HTML5 we wanted to move away from this dependence on Java Applets.

I wanted to use HTML5 Canvas for drawing images/annotations. Obvioulsy we figured using GWT would be the right choice for this. GWT however does not currently support Canvas element. There are some GWT canvas libraries GWTCanvas and others that provide GWT widgets. Given the nature of our application and the need for support on devices like ipad, iphone etc where the input mechanisms like multi-touch is needed, I figured a better model would be to actually implement Canvas as a CanvasElement. Widgets can then be built as needed. So I took the approach GWT Quake Port has taken and defined CanvasElement, CanvasRenderingContext2D etc. As mentioned earlier our goal was also to mimick the same functionality we had in JAI based applets - annotation of images on devices like Ipad .. using multi-touch inputs. I ended up using the same model Alex Moffet described for supporting mult-touch. Ended up adding gesture events as well to multi-touch events.

It took me just 2 weeks to get an initial implementation up and running. Worked great on Safari/Mac, Firefox and Chrome. The multi-touch functionality worked great on ipad as well. I also realized that touch provides a different and better interface so it does not make sense to simulate the same "mouse down/drag ..." or an icon to zoom but use natural touch events like pinch for zooming ... Here again GWT's power shows off. Ended up creating seperate widgets - one for touch devices like ipad and one for PCs/Macs .. mouse based devices and used GWT's deferred binding to have the right widget instantiated. Deferred binding also came in handy to overcome some of the issues we had with Safari on ipad. Altough Canvas is supposed to be supported fully - drawing text using Canvas does not work - or rather works incorrectly. So ended up using Label Widget and positioning them for ipad and used Canvas draw text for annotating images with text.

My experience with "extending" GWT showed how easy and powerful GWT framework is. Converting from Java Applet to "native" browser Canvas implementation using GWT was fairly straightforward. Turns out Canvas implementation is a big performance boost as compared to Java Applet implementation and also fewer issue with compatibility of Java version/JAI install issues not to mention that we will be able to support our document management on ipad, iphone ..etc. There is no way I would have been able to get a initial implementation working within 2 weeks without GWT. Also benefitted tremendously from all the users experiences and suggestions folks had on using GWT.

We will be releasing the "Canvas" version of document management next month. Canvas currently is not supported in IE8 but will be supported in IE9 - available in IE9 preview. Until IE9 is out, we will have our users who want to use IE using Google Chrome Frame. There are silverlight and excanvas implemenation to add on Canvas tag to IE but all those seem to be buggy/limited. Drawing text, complex arcs .. is neither supported or buggy - at least that's what my experience has been. I could not get my application - annotation part working using excanvas.js. The silverlight version is fast but still has issues. Google Chrome Frame came to the rescue. Works like a charm :-).

Thanks to Google GWT team for building such a powerful framework.

Thursday, July 29, 2010

Integrating GWT Services with Mirth/HL7

In my previous blog I talked about exposing GWT services to other protocol and languages. This has paid off big time for Medrium. In healthcare the predominant protocols used are HL7 which IMHO is hard to understand. HL7 makes simple problems very complex and convoluted to program to. Mirth is an open source project that has been around for quite a while and has built a robust and well tested platform for interoperability in healthcare. Mirth supports HL7 and provides a platform and tools that are well tested and used. Mirth has good tools built in to map HL7 to other formats like XML and vice-versa. Medrium already built functionality to expose GWT interfaces to XML which we ended up mapping - GWT XML interfaces to HL7 using mirth tools. Medrium used Mirth to map XML to HL7 and vice versa to provide connectivity to SureScripts for electronic prescriptions/messages functionality as well as importing Lab reports. Electronic prescriptions and Lab reports both use HL7 formats/versions and Medrium never had to program/understand HL7 formats but just understand enough to map the HL7 formats to GWT XML formats.


All our developers wrote GWT RPC services in Java and the XMLRPC functionality allowed Medrium to map the GWT Service method returns/calls to HL7 messages thru Mirth. Medrium will be using the same mechanism to import/export CCDs into and out of Medrium just using GWT RPC services. This shows that the model medrium chose - defining IDLs to expose interfaces and then mapping these interfaces to GWT RPC services was the right model. Exposing GWT services to XML not only facilitated our mapping of XML to HL7 using Mirth but has also provided a framework to open up interfaces to other vendors who can provide additional services to our clients.

Friday, April 30, 2010

Exposing GWT RPC services to other protocols and languages

During the design of our EMR application, we started laying out the interfaces in IDL. My prior background in CORBA and IDL had taught me that defining interfaces in IDL is good way of approaching the various interfaces without getting bogged down in programming language issues. It makes it easy for developers to go over the IDLs and once settled on the design, the work of implementing them can be distributed easily. Knowing that we would be using GWT, we used a subset of the IDL - no inouts or out parameters ..

Our hope was that once we defined these interfaces, clients - be it Browser (GWT client) or other programs can access these interfaces as well. The goal essentially was to be able to open up these interfaces to say XML-RPC or other protocols as well and be able to use the same security framework we have in place. I am happy to report that we were able to do this with GWT with ease.

All GWT RPC calls go thru to RemoteServiceServlet (or in our case SecureRemoteServiceServlet) servlets. GWT code then parses the request and executes the appropriate method. In order to support an XML based RPC call we modified the service call as follows.
public class SecureRemoteServiceServlet extends RemoteServiceServlet {
...
protected void service(HttpServletRequest req,
HttpServletResponse res)
throws ServletException, IOException {
if ( isXMLRequest(req) ) { //if servlet path is xml
doPostXML(req, res);
else {
super.service(req, res);
}
}
...
}
doPostXML parses the XML request, plugs into the existing security framework ..., calls the method and returns the response back as XML. The XML protocol and serialization of arguments and return types to XML was straight forward using reflection. We ended up using annotations on interface arguments so we can use these as element names in the XML.
Example:
public interface Patient extends SecureRemoteService {
@ParamName("PatientInfo")
public PatientInfo get(@ParamName("AccountID") int accountID,
@ParamName("PatientID") int patientID);
...
}
We then went one step further and decided it's better to provide language bindings for this XML-RPC, so that it makes it easy for developers to use these interfaces. We generated a Java Binding for the GWT service interfaces by adding a code generator that took as an argument a GWT Service interface and generated appropriate java code. We are in the process of generating a binding for Java Script and who knows - may be will provide a binding for Objective-C down the line. So now opening up an interface is just using the code generator for the language binding and adding appropriate access controls.

In summary designing interfaces using IDL/GWT services allowed us to easily open up our platform to other applications and languages.

Wednesday, April 28, 2010

GWT and Cross-Site Requests

Browsers have Same Origin Policy (SOP) which disallows Ajax (XMLHttpRequest) calls made to servers other than the ones where the page and/or javascript is loaded from. This is done for security reasons. Allowing calls to different servers than the ones where the page originated from could cause some web pages from sites to make calls onto other hosts which trust the client. SOP however causes a huge issue for GWT applications. GWT modules load just a single page and given SOP all the application needs have to be performed by the same server. This is not ideal if an GWT application is to make use of services provided by multiple servers. For example Medrium has Document Management, Practice Management, Mail, Clinical servers providing various services. If the Clinical Application needs to access a Document it will not be able to make a call onto Document Management server to retrieve the Document. The page containing this request has to be loaded from Document Management server or a proxy needs to be set up on Clincal server to retrieve the document and pass back to the Clinical GWT Client.

Given the need for Cross-Site requests W3C has proposed a Cross-Origin Resource Sharing specification which allows secure Cross-Site calls to be made -http://www.w3.org/TR/cors/. The specification allows the browser client and server to negotiate if access is allowed and make cross site calls. Most modern browsers – Firefox, Safari, Opera support this specification. This specification allows the same XMLHttpRequest to be used for both Cross-Site and same site calls. Microsoft however does not support XMLHttpRequest instead supports another API . IE8 supports XDomainRequest which can do Cross-Site requests but has a different API and also does not allow passing any Header information. On the server side, one can write a Servlet Filter which can respond to Client Cross Origin negotation requests. Jetty 7.x provides such a filter so no new filter needs to be written but Jetty's CrossOrigin filter can be configured via web.xml http://wiki.eclipse.org/Jetty/Feature/Cross_Origin_Filter. Cross Site requests are straight forward for the browsers that support W3C spec and would not require any support from GWT. However, since IE does not support W3C spec and has a completely different interface. So it is not possible to do Cross-Site requests from IE using GWT.

In order to support Cross Site requests for IE8 we first create a Javascript Overlay Type for XDomainRequest.
public class XDomainRequest extends JavaScriptObject {
public static native XDomainRequest create() /*-{
if ($wnd.XDomainRequest) {
return new XDomainRequest();
}
else {
$wnd.alert("Cross site requests not supported in this browser");
return null;
}
}-*/;
protected XDomainRequest() {}
public final native void abort() /*-{
this.abort();
}-*/;
public final native void clear() /*-{
var self = this;
$wnd.setTimeout(function() {
self.onload= new Function();
self.onprogress= new Function();
self.onerror= new Function();
self.ontimeout= new Function();
}, 0);
}-*/;
public final native String getContentType() /*-{
return this.contentType;
}-*/;
public final native void setTimeout(int value) /*-{
this.tmeout=value;
}-*/
public final native int getTimeout() /*-{
return this.timeout;
}-*/;
public final native String getResponseText() /*-{
return this.responseText;
}-*/;
public final native void open(String httpMethod, String url) /*-{
this.open(httpMethod, url);
}-*/;
public final native void send(String requestData) /*-{
this.send(requestData);
}-*/;
public final native void setHandler(XDomainRequestHandler handler) /*-{
var _this = this;
this.onload = $entry(function() {
handler.@com.medrium.gwt.client.rpc.XDomainRequestHandler::onLoad(Lcom/medrium/gwt/client/rpc/XDomainRequest;)(_this);
});
this.onerror = $entry(function() {
handler.@com.medrium.gwt.client.rpc.XDomainRequestHandler::onLoad(Lcom/medrium/gwt/client/rpc/XDomainRequest;)(_this);
});
this.ontimeout = $entry(function() {
handler.@com.medrium.gwt.client.rpc.XDomainRequestHandler::onTimeout(Lcom/medrium/gwt/client/rpc/XDomainRequest;)(_this);
});
this.onprogress = $entry(function() {
handler.@com.medrium.gwt.client.rpc.XDomainRequestHandler::onProgress(Lcom/medrium/gwt/client/rpc/XDomainRequest;)(_this);
});
}-*/;
}

A Domain request handler interface to handle callbacks from XDomainRequest
public interface XDomainRequestHandler {
public void onLoad(XDomainRequest req);
public void onProgress(XDomainRequest req);
public void onError(XDomainRequest req);
public void onTimeout(XDomainRequest req);
}

We then provide different implementations for GWT's RpcRequestBuilder, RequestBuilder, Request and Response classes
public class IECrossSiteRpcRequestBuilder extends RpcRequestBuilder {

protected RequestBuilder doCreate(java.lang.String serviceEntryPoint) {
return new IECrossSiteRequestBuilder(RequestBuilder.POST,
serviceEntryPoint);
}
/**
* Nothing to do. Cannot set custom headers in XDomainRequest
*/
protected void doFinish(RequestBuilder rb) {
}
}

public class IECrossSiteRequestBuilder extends RequestBuilder {
public IECrossSiteRequestBuilder(RequestBuilder.Method httpMethod,
String url) {
super(httpMethod, url);
}
public Request send() throws RequestException {
return doSend(getRequestData(), getCallback());
}
public Request sendRequest(String data, RequestCallback callback)
throws RequestException {
return doSend(data, callback);
}
private Request doSend(String data, final RequestCallback callback)
throws RequestException {
XDomainRequest xhr = XDomainRequest.create();
try {
xhr.open(getHTTPMethod(), getUrl());
}
catch (JavaScriptException e) {
RequestPermissionException requestPermissionException =
new RequestPermissionException(getUrl());
requestPermissionException.initCause(new RequestException(
e.getMessage()));
throw requestPermissionException;
}
// Cannot set content type on IE
final IECrossSiteRequest req= new IECrossSiteRequest(xhr);
req.setStatus(IECrossSiteRequest.OPEN);
final int timeout;
if ( (timeout = getTimeoutMillis()) > 0 ) {
xhr.setTimeout(getTimeoutMillis());
}
// set handlers
xhr.setHandler(new XDomainRequestHandler() {
public void onLoad(XDomainRequest r) {
req.setStatus(IECrossSiteRequest.DONE);
callback.onResponseReceived(req,
new IECrossSiteResponse(r));
}
public void onTimeout(XDomainRequest r) {
req.setStatus(IECrossSiteRequest.DONE);
callback.onError(req,
new RequestTimeoutException(req, timeout));
}
public void onProgress(XDomainRequest r) {
}
public void onError(XDomainRequest r) {
// Assume permission exception since XDomainRequest does not
// return an error reason
req.setStatus(IECrossSiteRequest.DONE);
callback.onError(req,
new RequestPermissionException(getUrl()));
}
});
try {
xhr.send(data);
req.setStatus(IECrossSiteRequest.SENT);
}
catch (JavaScriptException e) {
throw new RequestException(e.getMessage());
}
return req;
}
}

public class IECrossSiteRequest extends Request {
static final int UNSENT = 0;
static final int OPEN = 1;
static final int SENT = 2;
static final int DONE = 3;
private int _status = UNSENT;
private XDomainRequest _xhr;
void setStatus(int status) { _status = status; }
public IECrossSiteRequest(XDomainRequest xhr) {
if (xhr == null ) {
throw new NullPointerException();
}
_xhr = xhr;
}
public void cancel() {
if ( isPending() ) {
_xhr.abort();
}
}
public boolean isPending() {
return (_status == OPEN || _status == SENT);
}
}

public class IECrossSiteResponse extends Response {
private XDomainRequest _xhr;
public static class IEHeader extends Header {
private String _name;
private String _value;
public IEHeader(String name, String val) {
_name=name;
_value = val;
}
public String getName() { return _name; }
public String getValue() { return _value; }
}
public IECrossSiteResponse(XDomainRequest xhr) {
_xhr = xhr;
}

public String getHeader(String header) {
return header.equals("Content-Type") ? _xhr.getContentType() : null;
}
public Header[] getHeaders() {
if ( _xhr.getContentType() != null) {
Header ret[] = new Header[1];
ret[0] = new IEHeader("Content-Type",_xhr.getContentType());
return ret;
}
else {
return null;
}
}
public String getHeadersAsString() {
return ( _xhr.getContentType() == null ) ? ""
: ("Content-Type : " + _xhr.getContentType());
}
public int getStatusCode() {
return (_xhr != null) ? Response.SC_OK : Response.SC_BAD_REQUEST;
}
public String getStatusText() {
return "OK";
}
public String getText() {
return _xhr.getResponseText();
}
}

We can use deferred binding to replace RpcRequestBuilder, with IECrossSiteRpcRequestBuilder in the module definition for IE.
<module>
<inherits name='com.google.gwt.user.User'/>
...
<replace-with class="com.medrium.gwt.client.rpc.IECrossSiteRpcRequesrBuilder">
<when-type-is class="com.google.gwt.user.client.rpc.RpcRequestBuilder"/>
<when-property-is name="user.agent" value="ie8" />
</replace-with>
...
</module>

Cross site RPC calls can now be made by changing the URL on the async interface.
PatientConditionAsync pc = GWT.create(PatientCondition.class);
ServiceDefTarget target = (ServiceDefTarget)pc;
target.setServiceEntryPoint(crossSiteURL);
..

We still need to fix a few things on the server implementation, since the standard RemoteServiceServlet expects header information - Content-Type, X-GWT-Module-Base,X-GWT-Permutation. IE's XDomainRequest does not allow any headers so the standard service call in RemoteServiceServlet would reject the request. So we defined a new class that implementations which expect remote calls can derive from that get around this issue.

public class SecureRemoteServiceServletNoHeader extends
SecureRemoteServiceServlet {
public SecureRemoteServiceServletNoHeader() {}
protected void service(HttpServletRequest req,
HttpServletResponse res)
throws ServletException, IOException {
if ( req.getMethod().equals("POST") ) {
doPostNoHeader(req, res);
}
}
protected void doPostNoHeader(HttpServletRequest req,
HttpServletResponse res)
throws ServletException, IOException {
try {
synchronized(this) {
if ( perThreadRequest == null) {
perThreadRequest = new ThreadLocal();
}
if ( perThreadResponse == null) {
perThreadResponse = new ThreadLocal();
}
}
perThreadRequest.set(req);
perThreadResponse.set(res);
processPostNoHeader(req, res);
}
catch (Throwable e) {
RPCServletUtils.writeResponseForUnexpectedFailure(
getServletContext(), res, e);
}
finally {
perThreadRequest.set(null);
perThreadResponse.set(null);
}
}
public void processPostNoHeader(HttpServletRequest req,
HttpServletResponse res) throws ServletException, IOException,
SerializationException {
String payload = RPCServletUtils.readContentAsUtf8(req, false);
String respPayload = processCall(payload);
boolean gzipEncode = RPCServletUtils.acceptsGzipEncoding(req)
&& shouldCompressResponse(req, res, respPayload);
RPCServletUtils.writeResponse(getServletContext(),
res, respPayload, gzipEncode);
}
}

The model described here works for us on Chrome, Firefox, Safari, IE8 etc. On the server side we use Jetty and the Cross Origin Filter supplied by Jetty. On other web servers you can easily write a filter to accept the requests. On older browsers that do not support Cross Origin requests a redirect to the cross site URL may work (although we have not tried that). The redirect can be done easily using Servlet Filters.

Security Implementation

Our existing Security Framework is Role based. Each logged in user can have one or more roles. Example a logged in user can take on a role of a Receptionist or a Receptionist and a biller. Each role restricts the logged in user to a set of methods on a interface/class. Due to HIPAA requirements we also log out an user if session is idle for a specified amount of time. If a user is "logged out" and a user submits a request, the Security Framework presented the user with a login screen and once user logs in the request is re-queued to the server. This allows the user to not loose any data if the user entered in a lot of data. Our applications - Practice Management, Document Management etc .. are spread over different web servers and the Security Framework also allowed us a "Single Sign on". User can log in thru any application and can follow the links to other web servers without having to login again.

Our goal in moving to GWT was to ensure that all the above functionality stayed intact. Below is how we implemented similar functionality using GWT.
A Login interface provides login functionality and defined as
public interface Login extends RemoteService {
public UserSessionInfo login(String userid, String password);
public UserSessionInfo checkSession(String userid, String password);
...
public void logoff();
}

In order to implement the security framework we needed to intercept the calls onto the server implementation so we can authenticate the user role, access etc.
public class SecureRemoteServiceServlet extends RemoteServiceServlet {
 ...
public String processCall(String payload) throws SerializationException {
try {
RPCRequst rpcRequest = RPC.decodeRequest(payload, getClass());
Object parameters[] = rpcRequest.getParameters();
Method m = rpcRequest.getMethod();
authenticate(m, parameters);
return RPC.invokeAndEncodeResponse(this, m, parameters,
rpcRequest.getSerializationPolicy());
}
catch (Exception e) {
return RPC.encodeResponseForFailure(null, e);
}
}
protected HttpSession getSession() {
HttpServletRequest req = getThreadLocalRequest();
return (req=null) ? null : req.getSession());
}
protected void authenticate(Method m, Object Parameters[])
throws com.medrium.exception.SecurityException {
// Logic for verifying user login and access based on
// on user role ..
// Throw SecurityException if user is not logged in
// or user does not have access to method
..
}
..
public boolean allowAcess(String method)
throws com.medrium.exception.SecurityException {
}
}

All our server implementations derived from SecureRemoteServiceServlet.
public class PatientConditionImpl extends SecureRemoteServiceServlet
implements PatientConditon {

public void addConditions(ArrayList c) {
..
}
..
}
Note that the SecureRemoteServiceServlet may throw com.medrium.exception.SecurityException. However this exception is not defined in the interface methods. So GWT will not generate/include the serialization code for SecurityException as such you would get an exception on the client. Obviously we do not want to add throws SecurityException to each of the methods on the interface since we would like the security framework to redirect the user to login and reexecute the call (once logged in) if the security exception was a “not logged in” exception. To enable this functionality we did a couple of modifications.
Defined a new interface
public interface SecureRemoteService extends RemoteService {
public boolean allowAccess(String method)
throws com.medrium.exception.SecurityException;
}
and all our RPC interfaces derived from SecureRemoteService instead of RemoteService.
interface PatientCondition extends SecureRemoteService {
void addConditions(int accountID, ArrayList list);
PatientConditionInfo get(int accountID, int PatientConditionID);
ArrayList search(int accountID, SearchCriteria criteria);
}
This ensured that SecurityException serialization was possible in the client. The method allowAccess is implemented by SecureRemoteServiceServlet – which essentially will throw a SecurityException if user is not logged in or return true/false if user has access or no access to the given method.

One of the requirements discussed before was to ensure that the framework address the issue of directing the user to login if user session timedout (or user bookmarked a link) and once logged in automatically reexecuting the initial request. The Command pattern allows us to do this seemlessly and is done by the base Action class and a SecurityManager class as follows.
public class SecurityManager {
private UserSessionInfo _user = null;
private ArrayList<Action<?>> _actionQueue =
new ArrayList<Action<?>>();
private Provider<Login_checkSession> _loginCheckSessionProvider;
// We use Gin to get instances of Action classes although you can do a new ...
protected SecurityManager() {}
@Inject
public SecurityManager(... ) {
..
}
...
public UserSessionInfo getUser() {
return _user;
}
public void checkSession() {
Login_checkSession action = _loginCheckSessionProvider.get();
action.initialize(
new AsyncCallback<UserSessionInfo>() {
public void onSucess(UserSessionInfo user) {
if ( !user.loggedIn() .. ) { // User not logged in
displayLogin();
}
else {
setUser(userSession);
...
executeActions();
}
}
public void onFailure(Throwable t) {
...
}
});
action.execute();
}
private void displayLogin() {
// Binds the Login Presenter – displays login screen
}
public void reloginAndExecute(Action<?> action) {
_actionQueue.add(action);
// Only display login for the first action in queue
if ( _actionQueue.size() = 1) {
displayLogin();
}
}
private void executeActions() {
for (Action<?> action: _actionQueue) {
action.execute();
}
_actionQueue.clear();
}
}

Below are some changes made to the Command Pattern base Action class.
public abstract class Action<T> implements AsyncCallback<T> {
// Inject SecurityManager
@Inject static protected SecurityManager _securityManager;
...
public void onFailure(Throwable t) {
// If user not logged in redirect to SecurityManager so security manager
// can display login screen, execute Login RPC and once logged in
// reexecute the action.
if ( t instance of com.medrium.exception.SecurityException &&
((SecurityException)t).getReasonCode() == ..
SecurityException.NOT_LOGGED_IN ) {
_securityManager.reloginAndExceute(this);
}
else {
_callback.onFailure(t);
}
}
...
}

Given the nature of the application and a user role determining the functionallity available the client code needed a way to determine if a certain functionality (link) is available to the logged in user. It is not desirable to always enable a link and then issue an alert that the user does not have access. The client can make a allowAccess call on an interface to determine if a method is accessible to the logged in user before enabling a link. This however is not desirable since this would require additional RPC calls.

A model described below was used so we can “statically” generate some access control code.
Medrum's model for authenticating is based on tieing each interface name and method access to a role and optionally to a set of services enrolled by the account. The link between a role and interface.method is “static” - i.e. it cannot be changed dynamically. A new release may change these attributes. The link between enrolled service and interface.method however can change dynamically. i.e. the user may enroll a new service which would enable new functionality. For example an account administrator can enroll to a “Patient statement service” which would enable Statement printing functionality etc. The Command pattern code generator was modified so that it would generate the access masks for roles and services that are needed to perform an operation. Essentially, the code generator looked up the database tables contain the access defintions and generated an additional function isAccessAllowed.
public Class PatientConditionImpl extends SecureRemoteServiceServlet
implements PatientCondition {
public void addConditions(ArrayList conditions) {
...
}
public boolean isAcessAllowed() {
// Call security manager with roles mask, service mask ...
return _securityManager.isAccessAllowed(...);
}
}

Security manager changes are
public class SecurityManager {
public boolean isAccessAllowed(int roleMask, int servicesMask) {
return (rolesMask & _user.getRoleMask()) > 0
&& (serviceMask & _user.getServiceMask()) > 0;
}
}
One of the features we wanted was a "Single Sign On". This feature was easily implemented outside of GWT by using domain cookies and Servlet Filter and HttpSessionListener. When a user logs in, a domain cookie "SharedLogin" cookie was created. The Cookie value contained the host name and session id. When the user went to our other application web server, this "domain" cookie was passed by the browser. A servlet filter parsed the "SharedLogin" cookie and did a verify session - a simple Rest Https call to the hostname in the cookie value along with the session id. The servlet filter also updated the "SharedLogin" cookie value with it's own hostname, session id .. so that the "SharedLogin" contained the host and session information of the server that was accessed last.

In my next post, I will explain how we implemented Cross Site GWT RPC calls.

Tuesday, April 27, 2010

Command Pattern implementation

Our Clinical applications have HIPAA requirements that require a user to be logged out if there is no activity for a specified number of minutes. It is also common in our applications that Users do partial data entry and attend to Patients. If the session were to be "logged out" we want to preserve the data that was entered by the user so far and "requeue" the request once the User "re-logs" in. We implemented the command pattern described by Ryan so we can do the "requeuing" of requests on session timeouts/logout. Our goal was also to keep the Server implementation standard and also retain the strong typing provided by standard GWT RPC interfaces.

Defined an abstract class Action as
public abstract class Action<T> implements AsyncCallback<T> {
protected AsyncCallback<T> _callback;
protected Action() {}
protected void setCallback(AsyncCallback<T> callback) {
_callback = callback;
}
public void onSuccess(T result) {
_callback.onSuccess(result);
}
public void onFailure(Throwable t) {
_callback.onFailure(t);
}
public abstract void execute();

...
}
We wrote a simple Code Generator which took as argument an RemoteService interface and generated an Async Interface and an "Action" class for each method in the interface.
For Example if the following interface name was passed to the code generator
interface PatientCondition extends RemoteService {
void addConditions(int accountID, ArrayList<conditioninfo> list);
PatientConditionInfo get(int accountID, int PatientConditionID);
...
}
the code generator would use Java Reflection to generate the Async interface
interface PatientConditionAsync {
void addConditions(int p0, ArrayList<conditioninfo> p1, AsyncCallback<Void> ret);
void get(int p0, int p1, AsyncCallback<PatientConditionInfo> ret);
..
}
and a class for method addConditions
PatientCondition_addConditions extends Action<void> {
private PatientConditionsAsync _svc;
private java.util.ArrayList _p0;

public PatientCondition_addConditions(PatientConditionsAync svc){
_svc = svc;
}
public void initialize(java.util.ArrayList p0, AsyncCallback<void> callback){
_p0 = p0;
setCallback(callback);
}
public void execute() {
_svc.addConditions(_p0, this);
}
..
}
and another class for method get
PatientCondition_get extends Action<patientconditioninfo> {
...
}
The code generator is essentially generating "wrapper" classes for each method and caching the arguments and callback so it can "re-execute" the call if needed. In the next post I will describe how the Action classes interact with Security.

Monday, April 26, 2010

Building an EMR application with Google Web Toolkit

My company provides Revenue cycle management and Practice Management services to doctors. The platform was built using Java Servlet technology and has evolved into a fairly complex application over a decade. We were adding on additional services to our application - Electronic Medical Records and decided on GWT framework for this new clinical application. We have been watching GWT progress on and off for over a year and late last year decided to take the plunge - and we are glad we did. There a number of reasons why we chose Google Web Toolkit. The overriding factor being that managing Javascript was getting out of hand. GWT provides a really elegant way to "modularize" javascript and allows our development team to scale better.

We benefitted a lot from the vast amount of GWT articles, presentations and hope that our experience would benefit other future or current GWT developers.

In the next set of posts, I will describe how we implemented
Our implementation is somewhat different than a number of posts I have seen on implementing this design pattern.

b) Security and Single Sign-on - Ability to control access to Methods on the RPC interface.
Our existing application had a framework to control access to - Object/Method to a given user role and we wanted to use the same authentication framework.

c) Cross Site RPC calls - getting around the restriction of Same Origin Policy.
Most browsers support the CORS spec except for Microsoft Internet explorer - which supports XDomainRequest. Explain how we integrated Cross Site RPC into GWT to support IE as well.