Update: Patrick Chanezon, Google Developer Advocate, has supplied us with a video interview with Matthias, which can be found here.
Today's guest blog post comes from Matthias Büchner, Software Engineer at digital security company, Gemalto along with his colleagues: Colin Gormley, Jonas Pärt and Ella Segura. Here they discuss their Device Administration Service, and the recent port to GWT.
Introduction
The Device Administration Service (DAS) solution allows you to manage the personalization and lifecycle of smart card devices. It was developed using ASP .NET and native JavaScript as a hosted web application. Our team was asked to port the solution for deployment as both a hosted and an offline standalone system.
We had three major goals for the re-design:
Here was our strategy:
GWT was the obvious choice. It addresses all the goals we had outlined, plus we really looked forward to writing Java code again after living and breathing complex native JavaScript for so long. The prospect of using JUnit had us practically salivating. In this article, we will discuss how we designed our application with GWT and give a few tips that we learned while developing it.
A Smart Card Driven Architecture
A smart card is a secured electronic chip typically embedded in a plastic card or inside a USB dongle. Two examples of what a smart card can do are managing physical and logical access, and securing electronic transactions. Despite their small size, they offer a self-contained operating system and can run very advanced applications. The card is connected to the PC using a smart card reader and the low level communication between the reader and the smart card is done via an APDU.
Traditionally, you are required to install some platform-specific software on your computer before you can communicate with a smart card. This software is known as middleware, and typically takes up a large footprint and must be developed for specific environments. Gemalto developed a browser extension, called SConnect™ , which allows communication with a smart card using JavaScript. SConnect is available on most major operating systems and browsers. SConnect™ is to the smart card what AJAX is to the web. SConnect communicates asynchronously with a smart card, eliminating latency problems on the user interface. We anticipated that this feature would have a strong impact on our design approach.
We investigated the GwtEvent class and its callback mechanism to manage the asynchronous communications. An event is fired just once, and it is handled by one or more handlers. The one-to-many model fits perfectly with how we wanted to manage the notification mechanism when the card is either inserted or removed from the reader. We created card insertion and removal events and utilized an event bus to manage the event firing. Unfortunately, we found difficult to follow and debug event-driven code.
Callback mechanisms are better suited for one-to-one relationships between the callers and the callees. We opted to use callbacks to manage most of the business and low level logic, since they are mostly one-to-one relationships.
When several callbacks, that required data sharing, were chained in sequence things got a little ugly. We started with nested callbacks. As you can see in the AsyncCallbackExample class, the code flow can be difficult to follow and decipher. To solve the problem, we created the following Wrapper class. This class allows us to create a final instance of the data we need to share. Separate callbacks no longer had to be nested to access and change the data we are passing around.
public class Wrapper { private T value; public Wrapper() {} public Wrapper( T value ) { this.value = value; } public void setValue( T value ) { this.value = value; } public T getValue() { return value; } }
This class allowed us to improve the readability and ease the maintenance of our code. You can see in details how to use it in our class example.
Rebuilding the User Interface
Our goal was to move all our source code to Java/GWT; this includes the entire user interface that was designed originally with ASP controls. We looked for an existing widget library since we did not want to spend a lot of time developing custom components. ExtJs GWT was a library that some of us were already familiar with, so it was a natural choice.
From a bird's eye view, most of the interface looks the same. Take a closer look, and you'll find that much of the details have been improved.
Data are loaded and updated asynchronously. Status messages and popup dialogs are now more like a desktop application. One of our goals was to keep the source code modular and easy to maintain. We were all excited at the prospect of writing code in a single language. The benefits of moving from source code written in ASP .NET + native JavaScript + CSS + HTML to Java are many. We especially were attracted to compile time checks, unit tests and writing all our code in a professional IDE.
We knew we wanted to structure our code in a way that would allow us to decouple the interface from the business logic. The video from Google I/O 2009 on best practices for architecting GWT app pointed us in the right direction. We adopted the MVP pattern and divided our UI code into presenters and displays using the gwt-presenter library.
In the DAS application, there is more than one presenter at work on any single screen. We needed the presenters to interact with each other but did not want them too tightly coupled. To address this issue, we brought in the event bus. Instead of presenters listening to events being fired by other presenters, now everyone alerts the event bus when something significant happens and registers themselves to the event bus for any events they are interested in.
The DAS application is currently localized for three languages. Implementing the Constants and Messages interfaces was very straightforward. We put all the text in an Excel spreadsheet for delivery to our translators and converted it into Property files.
We hit a small bump in the road when we realized that GWT does not take language settings of the browser into account for localization. You have to pass in ?local=xx in the URL when launching the application. To solve this problem, we had to take two approaches. For the hosted solution, we can easily parse the request header for language information and pass that into GWT using the Meta tags. For the standalone solution, where we cannot send any HTTP request, we settled for reading the system language settings from JavaScript and passing that information into GWT.
A Testing Strategy
There is a consensus in the developer community on the great benefits that unit tests provide. Unfortunately there is no good JavaScript unit test framework. In the ASP .NET version of the project, validation was done manually. A device configuration issue or similar problem would put the result off its intended target. Achieving measurable test coverage was not possible. Porting our application to GWT helped solve this problem. We just lost our last argument for not writing unit tests.
One aspect of the DAS user interface which is different from traditional web applications is that we have to interact with a smart card. Almost every function of the application requires a smart card to be inserted and some features require two smart cards. The smart card itself is a difficult device to test, it needs to be inserted into a smart card reader and much of the website is driven by smart card removal and insertions. Automating the device would require some sort of smart card or reader emulator that would trick the system into believing that the device was inserted. The technology involved is very low level, and emulating the interface is not a trivial task. We had to look for another solution.
Here is an overview of our application architecture.
We abstracted the Smart Card Layer to mock device interactions to higher layers and kept some of the manual (or semi-manual) tests for the lower layers. It divided out tests into two categories; unit tests and integration tests. Unit tests would use mocked Smart Card layer while the integration tests would require real smart cards to be inserted into readers during test execution.
Designing the Smart Card Layer
To enhance the testability of our code, we wanted to be able to mock parts of it that requires a smart card. To be mock-able, the Smart Card Layer needed a contract with the upper layer, we put in place an abstract base class that would expose functional methods and at the same time lock down how the result and potential errors were to be reported. Any class inheriting from the base class would be responsible for the device interaction.
Here is an extract of the base class:
protected abstract void getSerialNumber(); public final void getSerialNumber(AsyncServiceCallback callback) { ... getSerialNumber(); ... } protected void onGetSerialNumberFailure(ServiceError error) { getSerialNumberCallback.onFailure(new GetSerialNumberResult(error)); } protected void onGetSerialNumberSuccess(String serialNumber) { getSerialNumberCallback.onSuccess(new GetSerialNumberResult(serialNumber)); }
This contract would allow us to write an implementation for every type and variation of smart cards we needed to support, with the added bonus of allowing us to add a mock smart card layer implementation. The mock smart card implementation doesn't actually perform any smart card operations. Instead, we configure in the setup code of a unit test the expected input and return values for the test case.
Here is an extract of the mocked class extending the abstract class shown above.
public MockConfiguration getSerialNumberConfig = new MockConfiguration(); @Override protected void getSerialNumber() { getSerialNumberConfig.execute(new RunMe(){ @Override public void run() { if (getSerialNumberConfig.isSuccess()) { onGetSerialNumberSuccess(getSerialNumberConfig.returnValue); } else { onGetSerialNumberFailure(getSerialNumberConfig.serviceError); } } }); }
Unit tests
A unit test sets up the underlying mock classes for the test scenario and then makes the call to the subject of the test. These tests allowed us to validate our business logic in different use cases independent of a smart card. In effect we were able to make sure that our application would fail gracefully if the smart card behaved in an unexpected manner.
We used the code coverage tool EMMA to assert that every anticipated (and unanticipated) failure condition worked properly.
Here is how we configure the mocked class:
ConfigurableMscmMock mscmMock = new ConfigurableMscmMock(); mscmMock.getSerialNumberConfig.returnValue = "112211221122112211221122";
Using the above techniques, we exceeded our expectations of automated test coverage.
Conclusion
Overall, we are impressed and excited about what is possible with GWT. We were able to rebuild a complex browser-based application and deliver a consistent product for both the offline and hosted solutions with improved user experience and testing methodology.
The new DAS is one of the first smart card applications built with GWT at Gemalto. This project has proved to be a great learning experience for the team. We hope the foundation we built here will be a spring board for many more GWT applications to come.
Thank you for reading our article - for more information about DAS, please visit http://www.gemalto.com.