Saturday, March 27, 2010

Creating a Connect-Request in HealthVault

I've been doing a lot of work with Microsoft HealthVault at my job lately. I did some experimental work with the platform when I first started working there and since then, I've sort of been the team's HealthVault expert. It's been fun learning all about it and challenging as well, since most of the HealthVault libraries, tutorials, etc are centered around .NET (we use Java). So HealthVault is something I think would be fun to blog about.

This blog post will be about how to use what are called connect-requests in HealthVault. It assumes that you already have some knowledge of HealthVault from a developer's perspective. It includes Java code samples that use the JAX-B classes from the HealthVault Java Library.

Connect-requests are used by non web-based applications to create a connection to a HealthVault record. The process must only be completed once--not every time the application wants to access the record. They work like this:



1 The application prompts the user for the following information:

  • Friendly name - This can be anything, but should be the name that's on the HealthVault record.

  • Question - A question of the user's choosing (such as "What high school did I go to?").

  • Answer - The answer to the above question.

2 The application uses the above information, along with an external-id, to create a connect-request. The external-id can be any value that uniquely identifies the connect-request (the current time in milliseconds works fine). This value must be saved somewhere, as the application will need to use it again later (see step 5). Using these four bits of information, the application sends the request to HealthVault:
//create the request
CreateConnectRequestRequest request = new CreateConnectRequestRequest();
request.setExternalId(System.currentTimeMillis() + "");
request.setQuestion("Question?");
request.setAnswer("Answer");
request.setFriendlyName("Joe Smith");

//send the request / get the response
SimpleRequestTemplate srt = new SimpleRequestTemplate(ConnectionFactory.getConnection());
CreateConnectRequestResponse response = (CreateConnectRequestResponse) srt.makeRequest(request);
String identityCode = response.getIdentityCode();





Note:
Because we're using the JAX-B request classes, be sure to use
com.microsoft.hsg.methods.jaxb.SimpleRequestTemplate
and not
com.microsoft.hsg.request.SimpleRequestTemplate


3 HealthVault returns an identity-code, which is a sequence of 20 random letters separated into five groups of four:
DHLE-ELHP-AQLG-TLPZ-PQKD
The application must show this to the user. It should also show (what I call) the patient connect URL. The user will have to visit this URL in order to validate the connect-request:
https://account.healthvault-ppe.com/patientwelcome.aspx

Remove the "-ppe" when going live of course.

4 The user visits the patient connect URL in a web browser. This page will step the user through a process, requiring her to (1) login to her HealthVault account, (2) enter the identity-code, (3) enter the question/answer, and (4) choose which record in her account to grant the application access to.

5 Once the user has done this, the application is able to retrieve the person-id and record-id of the user's HealthVault record:
String externalId = //retrieve the external-id you saved in step 2
GetAuthorizedConnectRequestsRequest request = new GetAuthorizedConnectRequestsRequest();
SimpleRequestTemplate srt = new SimpleRequestTemplate(ConnectionFactory.getConnection());
GetAuthorizedConnectRequestsResponse response = (GetAuthorizedConnectRequestsResponse) srt.makeRequest(request);
for (ConnectRequest cr : response.getConnectRequest()) {
if (cr.getExternalId().equals(externalId)) {
String personId = cr.getPersonId();
String recordId = cr.getRecordId();
//save to persistent storage (like a database)
break;
}
}
However, the application has no way of knowing when the user will approve the application (when she completed step 4). So, the application must continually poll HealthVault for newly approved connect-requests (e.g. a separate thread which calls GetAuthorizedConnectRequests every 30 seconds or so).




Note:
The CreateConnectRequest method has a "call-back-url" parameter, which is a URL that HealthVault is supposed to call when the connect-request is validated by the user. However, at the time of this writing, it is not supported.

Monday, March 22, 2010

Mac OS X Mouse Acceleration

If the mouse acceleration settings of Mac OS X ever frustrate you, check out the Mouse Acceleration Preference Pane. This is a utility created by Christian Zuckschwerdt which lets you adjust these settings to your liking.

Apple changed around OS X's mouse API when version 10.6 was released, which made existing mouse acceleration tools useless. However, Christian just recently released an updated version supporting 10.6! My hand feels less cramped already...

Sunday, March 14, 2010

Working with Scala and Maven

In order to get Maven to properly recognize Scala code, there are are a number of steps you must take. Included below are pom.xml samples, along with explanations.

1. Name your source directories properly:
<project><build>

<sourceDirectory>src/main/scala</sourceDirectory>
<testSourceDirectory>src/test/scala</testSourceDirectory>

</build></project>

2. Add the scala-tools.org repositories.

These are required in order to download the necessary Scala dependencies (see step #3):
<project>
<repositories>
<repository>
<id>scala-tools.org</id>
<name>Scala-tools Maven2 Repository</name>
<url>http://scala-tools.org/repo-releases</url>
</repository>
</repositories>

<pluginRepositories>
<pluginRepository>
<id>scala-tools.org</id>
<name>Scala-tools Maven2 Repository</name>
<url>http://scala-tools.org/repo-releases</url>
</pluginRepository>
</pluginRepositories>
</project>

3. Add the appropriate dependencies:
<project><dependencies>

<!--
This contains the scala compiler, which Maven will use to compile your code and run your unit tests.
-->
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>2.7.7</version>
</dependency>

<!--
This is Scala's unit testing framework. It's also possible to use JUnit, but when in Rome, right?
-->
<dependency>
<groupId>org.scalatest</groupId>
<artifactId>scalatest</artifactId>
<version>1.0</version>
<scope>test</scope>
</dependency>

<!--
If you want to use scalatest, unfortunately you also need to include JUnit as a dependency (see step #5).
-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>

<dependencies></project>

4. Add the scala-tools plugin:
<project><build><plugins>

<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>

</plugins></build></projects>

5. Add the @RunWith annotation to each unit test.

If you are using scalatest as your unit testing framework, you must trick Maven into thinking that your tests are JUnit tests. Otherwise, Maven will not run your tests:
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
import org.scalatest.FunSuite

@RunWith(classOf[JUnitRunner])
class MyScalaTest extends FunSuite{
//...
}

Tip: If you're using Eclipse, you can right click on one of these unit tests and select "Run As > JUnit Test" to manually run the test.

Wednesday, March 10, 2010

XPath and Java

XPath is a domain specific language which is used to extract data from an XML document. It's supported by many different general purpose languages like C#, PHP, and Java. Take the following XML document for example:

<library>
<book>
<language>en</language>
<title>Solaris</title>
<author>Stanislaw Lem</author>
</book>
<book>
<language>fr</language>
<title>Le Petit Prince</title>
<author>Antoine de Saint-Exupéry</author>
</book>
<book>
<language>en</language>
<title>Dune</title>
<author>Frank Herbert</author>
</book>
</library>

The following XPath query retrieves the titles of all English-language books:

/library/book[language='en']/title

Java makes working with XPath (and XML in general) kind of complicated, since many different classes are involved. First, the XML document must be loaded into a DOM (Document Object Model).

StreamSource source = new StreamSource(new File("books.xml"));
DOMResult result = new DOMResult();
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(source, result);
Node documentRoot = result.getNode();

This means that the XML text is read into memory and organized into a tree of nodes where each tag is an element node. The top element node would be the <library> element, which would have three child element nodes (<book>), and so on.



To demonstrate the power of XPath, this is what Java code might look like if XPath did not exist. The programmer would have to manually iterate through the entire DOM to get what she needed.

List<String> englishTitles = new ArrayList<String>();
Node books = documentRoot.getFirstChild();
if (books.getNodeName().equals("library")){
for (int i = 0; i < books.getChildNodes().getLength(); ++i){
Node book = books.getChildNodes().item(i);
if (book.getNodeName().equals("book")){
boolean english = false;
String title = null;
for (int j = 0; j < book.getChildNodes().getLength(); ++j){
Node bookChild = book.getChildNodes().item(j);
if (bookChild.getNodeName().equals("language") &&
bookChild.getTextContent().equals("en")){
english = true;
}
if (bookChild.getNodeName().equals("title")){
title = bookChild.getTextContent();
}
}
if (english && title != null){
englishTitles.add(title);
}
}
}
}
for (String title : englishTitles){
System.out.println(title);
}

As you can see, this is very very tedious and error prone! XPath is the better solution by far:

XPath xpath = XPathFactory.newInstance().newXPath();
NodeList nodeList = (NodeList)xpath.evaluate("/library/book[language='en']/title",
documentRoot, XPathConstants.NODESET);
for (int i = 0; i < nodeList.getLength(); ++i){
Node node = nodeList.item(i);
System.out.println(node.getTextContent());
}

Namespaces

XML documents often use namespaces. These are sort of like Java packages--they group related elements together and prevent name collisions from occurring. Let's say that each <language> element belonged to a namespace:

<book>
<language xmlns="http://translate.google.com">en</language>
<title>Solaris</title>
<author>Stanislaw Lem</author>
</book>

Note: While namespaces technically can be anything (like "abc123" for example), they should be globally unique. There's no way to enforce this, so the convention is to use a URI belonging to the person or company creating the namespace. For example, if Oracle wants to use a namespace, they can be fairly certain that no one else in the entire world is using one starting with "http://www.oracle.com".


To make Java aware of namespaces,a NamespaceContext object must be created and added to the XPath object.

XPath xpath = XPathFactory.newInstance().newXPath();
xpath.setNamespaceContext(new NamespaceContext() {
public String getNamespaceURI(String prefix) {
if ("tr".equals(prefix)){
return "http://translate.google.com";
}
return null;
}
public Iterator getPrefixes(String uri) {
return null;
}
public String getPrefix(String uri) {
return null;
}
});

This will assign the prefix "tr" to the namespace "http://translate.google.com". The prefix can be anything, but the namespace must match the one in the XML document.

NodeList nodeList = (NodeList)xpath.evaluate("/library/book[tr:language='en']/title",
documentRoot, XPathConstants.NODESET);
for (int i = 0; i < nodeList.getLength(); ++i){
Node node = nodeList.item(i);
System.out.println(node.getTextContent());
}

To learn more about XPath, you can visit w3schools.com.

Tuesday, March 9, 2010

Dropbox


I just found out about this cool service called Dropbox, which I thought I'd write about. I read a blurb about it in the March issue of Linux Journal.

Dropbox is a free service that lets you store files online. That sounds pretty boring. But it's more than just a free FTP server. What happens is, you install a small app on your computer, which creates a special "dropbox" folder in your home directory (or in My Documents if you're using Windows). The app monitors this folder for any changes and syncs these changes with the Dropbox server. For example, when you copy a file (Word document, MP3, whatever) to this folder, it will immediately upload it to your Dropbox account. If you delete a file, it will delete it from your account.

But the really cool thing is that you can connect multiple computers to your account by installing the Dropbox application on each one (there are Windows, Mac, Linux, and iPhone versions). So if you add a file to your Dropbox folder on your desktop PC, it will immediately download to your MacBook laptop, Linux Netbook, and whatever else.