Showing posts with label LifeSensor. Show all posts
Showing posts with label LifeSensor. Show all posts

Wednesday, January 7, 2009

Flex Web Service Introspection Wizard and BlazeDS

In my previous post, I mentioned that if you want to fully use Flex 3.0 Web Service introspection wizard, you will need to either use Adobe LifeCycle Data service, or have a cross domain file installed on the server that expose the web services you want to use.

However, if you use only BlazeDS, the web service wizard can still be useful to better understand which type of objects you obtain when calling 3rd party web services (besides looking at the wsdl file and debugging ResultEvent.result content).

In this post I will describe how to use Flex Builder 3.0 to introspect the ICW LifeSensor Web Service API. Then I will build a small Flex based portlet to display information related to a patient who has his medical information stored in the LifeSensor Personal Health Record (PHR).

A. Introspecting the Web Services

For this, you will need to know the WSDL URL of your web services.

In the case of LifeSensor, I am accessing the WSDL file over HTTPS which is protected with a login and password but you can also test the intropection wizard with free available web services available on the internet.

From Flex builder (I am using Flex Eclipse plugin), select "Data/Import Web Service (WSDL)...":






Then select the folder you want to import your classes to, click next, then enter the WSDL URL and click next again:















First you select the list of the operations you want to import. In my case, I just want to import the operation findAccessibleRecords.

You can also change the default value of the packages for the classes that are going to be generated and the main class name.

In my case, I just kept the default values, respectively com.lifesensor and RecordModuleWebServiceImplService.


















It just takes few seconds to generate the proxy classes:




















Even though I am importing only one operation from LifeSensor, a little bit more than 80 classes are generated.




















RecordInfoXto
and its dependent classes structure is very close to the object returned by the web service call. Therefore I will be using only the following files:
  • AddressXto.as
  • CodeSystemXto.as
  • CodeXto.as
  • DateXto.as
  • EmbeddedObjectXto.as
  • RecordInfoXto.as









/**
 * RecordInfoXto.as
 * This file was auto-generated from WSDL by the Apache Axis2 generator modified by Adobe
 * Any change made to this file will be overwritten when the code is re-generated.
 */

package com.lifesensor
{
    import mx.utils.ObjectProxy;
    import flash.utils.ByteArray;
    import mx.rpc.soap.types.*;
    /**
     * Wrapper class for a operation required type
     */
   
    public class RecordInfoXto extends com.lifesensor.EmbeddedObjectXto
    {
        /**
         * Constructor, initializes the type class
         */
        public function RecordInfoXto() {}
           
        public var academicTitle:String;
        public var address:com.lifesensor.AddressXto;
        public var birthDate:com.lifesensor.DateXto;
        public var birthPlace:String;
        public var familyName:String;
        public var gender:com.lifesensor.CodeXto;
        public var givenName:String;
        public var middleName:String;
        public var scope:String;
        public var subjectId:String;
    }
}
   public class AddressXto extends com.lifesensor.EmbeddedObjectXto {
  /**
   * Constructor, initializes the type class
   */
  public function AddressXto() {}
          
  public var city:String;
  public var corpus:String;
  public var country:com.lifesensor.CodeXto;
  public var flat:String;
  public var line1:String;
  public var line2:String;
  public var organization:String;
  public var postalCode:String;
  public var state:com.lifesensor.CodeXto;
  public var streetAddressLine:String;
  public var zipCodeExtension:String;
 }

        public class CodeXto extends com.lifesensor.CodeSystemXto
 {
  /**
   * Constructor, initializes the type class
   */
  public function CodeXto() {}
          
  public var key:String;
 }

 public class DateXto extends com.lifesensor.EmbeddedObjectXto
 {
  /**
   * Constructor, initializes the type class
   */
  public function DateXto() {}
          
  public var isoDate:String;
 }

B. Creating the Flex component using BlazeDS

In a previous post, I have described in details how to create a BlazeDS application that uses BlazeDS to access web services. This one is very similar.

The proxy-config.xml describes the web service end-points and channel:

<destination id="ws-lifesensor-record">
        <properties>
            <wsdl>https://record2.us.lifesensor.com/phr/services/v2-5-0/RecordWebService?wsdl</wsdl>
            <remote-username>????????</remote-username>
            <remote-password>????????</remote-password>
            <soap>https://record2.us.lifesensor.com/phr/services/v2-5-0/RecordWebService</soap>
        </properties>
        <adapter ref="soap-proxy"/>
    </destination>


First, I import the generated classes. Then populating the RecordInfoXto object is straightforward:
import com.lifesensor.*;

private function findAccessibleRecords_result(event:ResultEvent):void {

  if (event.result != null) {
    var all_records:ArrayCollection = event.result as ArrayCollection;
    var record:Object = all_records.getItemAt(0);
                    
    // State
    var state:CodeXto = new CodeXto();
    state.key = record.address.state.key;
                    
    // Country
    var country:CodeXto = new CodeXto();
    country.key = record.address.country.key;
                    
    // Address
    var address:AddressXto = new AddressXto();
    address.streetAddressLine = record.address.streetAddressLine;
    address.city = record.address.city;
    address.postalCode = record.address.postalCode;
    address.state = state;
    address.country = country;
                    
    // Gender
    var gender:CodeXto = new CodeXto();
    gender.key = record.gender.key;

    // Birth Date
    var date:DateXto = new DateXto();
    date.isoDate = record.birthDate.isoDate;
                    
    // Record 
    patient_record = new RecordInfoXto();
    patient_record.givenName = record.givenName;
    patient_record.familyName = record.familyName;
    patient_record.gender = gender;
    patient_record.address = address;
    patient_record.birthDate = date;
    }
}

The resulting Flex based portlet is very simple (with a very compact code):

Thursday, December 18, 2008

BlazeDS and secure Web Service access

In a previous post, I described how to use Flex/BlazeDS to a access remote Web Services.
This time, I am explaining how to access a secure Web Service that requires basic authentication using the same mechanism. This involves additional changes in the configuration files.

My goal is to create flex components that access ICW Lifesensor public Web services.
The additional complication is that the access to wsdl file required authentication over HTTPS.

Fortunately, I have the login and password of a Lifesensor Account (patient), so I can use them
to access the Reporting Web Services in order to retrieve medical data entries. More information about the Lifesensor APIs and Web services are available on the ICW Developer Network.

To illustrate my point, I will use a very simple Web Service call getVersion that return some general information about the web services such as the Axis version and build date (Lifesensor uses the open source Apache Axis framework to provide Web Services).

In the wsdl file for Version, the port description shows that the getVersion operation does not have any parameter, so the call to the Web Service will be straightforward:
<wsdl:porttype name="Version">
 <wsdl:operation name="getVersion">
  <wsdl:input message="impl:getVersionRequest" name="getVersionRequest"></wsdl:input>
  <wsdl:output message="impl:getVersionResponse" name="getVersionResponse"></wsdl:output>
  </wsdl:operation>
</wsdl:porttype>

BlazeDS offers a Proxy to access remote servers. This is necessary, if you do not have a crossdomain.xml file on your remote server. As a result, there will be two hops. One from the shockwave component on the client to the Proxy, the other one from the Proxy to the remote server where the Web Services reside.

For security reasons, both bops have to be secure, as a result, the initial SWF access and loading has to be done through HTTPS. In my case, my application (and the BlazeDS proxy) is served by Tomcat 6.0 that has been configured for SSL.

The first configuration change is in the proxy-config.xml. Besides the fact that the URL is now using HTTPS protocol, you will need also to specify the soap URL instead of using a wildchard in order to avoid a RPC Fault since "a destination that allows multiple domains or ports does not allow authentication".

Also, since LifeSensor is using basic access authentication, the easiest way to avoid the pop-up window from your browser asking you for the login and password (especially for the first hop, which is not relevant), is to set them in the proxy-config.xml initially via remote-user and remote-password tags.
     <destination id="ws-lifesensor-version">
       <properties>
           <wsdl>https://record2.us.lifesensor.com/phr/services/Version?wsdl</wsdl>
           <remote-username>?????</remote-username>
           <remote-password>?????</remote-password>
           <soap>https://record2.us.lifesensor.com/phr/services/Version</soap>
       </properties>
       <adapter ref="soap-proxy"/>
   </destination>

The MXML flex file is not very different from a Web Service access with no authentication. The following code is just specific to the Lifesensor Web Service API:
<mx:Script>
     <![CDATA[
       ...
       private function getData():void { webService_LS_Version.getVersion.send();}   
       ...
    ]]>
   </mx:Script>
   <mx:WebService id="webService_LS_Version" destination="ws-lifesensor-version" useProxy="true">
       <mx:operation name="getVersion"
               resultFormat="object"
               result="getData_result(event);"
               fault="getData_fault(event);">
       </mx:operation>
   </mx:WebService>

Calling the Version Web Service from LifeSensor (via an application on https://localhost:8443/) returns the following text:
"Apache Axis version: 1.4
Built on Apr 22, 2006 (06:55:48 PDT)"

In addition to this, my recommendations will be to use Flex remote debugging and a HTTP debugging proxy such as Charles or Fiddler which can be very handy to understand and debug AMF and SOAP based HTTP wrapped requests.

Also, Flex Builder 3.0 has very nice Web Service Introspection tool. Unfortunately, you will need to have a cross domain file on the server you want to introspect or have LifeCycle Data service to use the generated proxies. Apparently, if you only use BlazeDS, there are no direct ways to use the generated code out of the box. However, you can use some of the generated classes to store some of the data you obtain from the Web Services. This will be the topic of my next post.