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):

8 comments:

D$ said...

I made a post about this showing how you can install BlazeDS on a remote server and also fully use WSDL Introspection with it.
BlazeDS & WSDL Introspection

Olivier said...

Hi I'm trying so hard to set up the use of an HTTPS WS. I got two others WS that works fine but when I specify the HTTPS WSDL the wizard tells me that it can't load the file. Checking on localhost/jbossws/services, I can access the WSDL file on the specified URL ..
Do you know if there's any particular manipulation to do ?
Thanks for reply

fandry said...

Hi Olivier,

I you want to use a secure connection (e.g. SSL) between your browser and your server, then you need to manage the correct authentication. If you are using BlazeDS, you can use it as a proxy to access secured Web Services - see my previous post on this topic

Olivier said...

Hello and thanks for ur reply. Sorry that it takes me some time to test and understand differents aspects of the SSL and WS over SSL, especially with FlexBuilder and JBoss which is quite new for me.
Anyway, I tried many possibilities and think I really have a problem with FlexBuilder or the configuration over SSL.

The error I got in the Flex Builder log is :

Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

But if I import an HTPS WS from Amason it works well.
I imported the certificate into the jre/lib/security/cacerts but doesn't change anything.

My question is : Is the cross domain necessary for the wizard or not? And if yes, is it OK to put it into the ROOT.war folder of JBoss or is this location wrong ?

Hope u'll be able to help me for that :)

Olivier said...

Hey there
I re-post a new comment for my problem and finally found the solution !!

The problem was that the client didn't know the server certificate ... and using the Import Wizard the client is actually Flex Builder that has his own cacerts file. I tried to import the certificate into the cacerts of the jdk but I was wrong, it should be done into the cacerts of Flex Builder. .. So I finally succeed to generate my own classes :)
Thanks for your help

abhishek said...

Hi,
I have developed the dashboard in my application using flex 3.0. For this I have used JSP wrapper around the flex application. My application runs on JBoss application server. For communication between flex app and my application I am using LCDS. HTTPService component is being used to receive data from the server. Channel definitions are given in service-config.xml for secure amf and secure http channels. In my proxy-config.xml I have defined Channels and destinations.

In my development environment both secure and non secure mode were working fine.
Now in my test environment I have deployed it behind the hardware load balancer(which accepts secure requests only and if the request is not secure it redirects it to secure url). I am getting following exception

06:06:13,521 INFO [STDOUT] [LCDS][ERROR] Exception when invoking service 'proxy-service': flex.messaging.services.http
.proxy.ProxyException: Error sending request. sun.security.validator.ValidatorException: PKIX path building failed: sun
.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
incomingMessage: Flex Message (flex.messaging.messages.HTTPMessage)
method = GET
url = http://10.4.10.224:80/kr/servlet/DashboardServlet
headers = {DSEndpoint=my-secure-http, DSId=A96D29E3-9B09-8156-DE28-257F324A283F}
clientId = A96D2A56-5A0D-4442-1E1B-4F62ACA8F33B
destination = dashboardService
messageId = 3E568EBD-2239-9A43-8D2D-B9B934E8F10B
timestamp = 1257246373114
timeToLive = 0
body = {source=default}
hdr(DSEndpoint) = my-secure-http
hdr(DSId) = A96D29E3-9B09-8156-DE28-257F324A283F
Exception: flex.messaging.services.http.proxy.ProxyException: Error sending request. sun.security.validator.Validator
Exception: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at flex.messaging.services.http.proxy.RequestFilter.sendRequest(RequestFilter.java:493)
at flex.messaging.services.http.proxy.RequestFilter.invoke(RequestFilter.java:84)

Thanks

Abhishek

abhishek said...

Hi,
I have developed the dashboard in my application using flex 3.0. For this I have used JSP wrapper around the flex application. My application runs on JBoss application server. For communication between flex app and my application I am using LCDS. HTTPService component is being used to receive data from the server. Channel definitions are given in service-config.xml for secure amf and secure http channels. In my proxy-config.xml I have defined Channels and destinations.

In my development environment both secure and non secure mode were working fine.
Now in my test environment I have deployed it behind the hardware load balancer(which accepts secure requests only and if the request is not secure it redirects it to secure url). I am getting following exception

06:06:13,521 INFO [STDOUT] [LCDS][ERROR] Exception when invoking service 'proxy-service': flex.messaging.services.http
.proxy.ProxyException: Error sending request. sun.security.validator.ValidatorException: PKIX path building failed: sun
.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
incomingMessage: Flex Message (flex.messaging.messages.HTTPMessage)
method = GET
url = http://10.4.10.224:80/kr/servlet/DashboardServlet
headers = {DSEndpoint=my-secure-http, DSId=A96D29E3-9B09-8156-DE28-257F324A283F}
clientId = A96D2A56-5A0D-4442-1E1B-4F62ACA8F33B
destination = dashboardService
messageId = 3E568EBD-2239-9A43-8D2D-B9B934E8F10B
timestamp = 1257246373114
timeToLive = 0
body = {source=default}
hdr(DSEndpoint) = my-secure-http
hdr(DSId) = A96D29E3-9B09-8156-DE28-257F324A283F
Exception: flex.messaging.services.http.proxy.ProxyException: Error sending request. sun.security.validator.Validator
Exception: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at flex.messaging.services.http.proxy.RequestFilter.sendRequest(RequestFilter.java:493)
at flex.messaging.services.http.proxy.RequestFilter.invoke(RequestFilter.java:84)
at flex.messaging.services.http.proxy.SecurityFilter.invoke(SecurityFilter.java:63)
at flex.messaging.services.http.proxy.ResponseFilter.invoke(ResponseFilter.java:68)
at flex.messaging.services.http.proxy.ProxyContextFilter.invoke(ProxyContextFilter.java:58)
at flex.messaging.services.http.proxy.AccessFilter.invoke(AccessFilter.java:59)
at flex.messaging.services.http.proxy.ErrorFilter.invoke(ErrorFilter.java:44)
at flex.messaging.services.http.HTTPProxyAdapter.invoke(HTTPProxyAdapter.java:543)
at flex.messaging.services.HTTPProxyService.invokeHttp(HTTPProxyService.java:353)
at flex.messaging.services.HTTPProxyService.serviceMessage(HTTPProxyService.java:181)
at flex.messaging.MessageBroker.routeMessageToService(MessageBroker.java:1503)
at flex.messaging.endpoints.AbstractEndpoint.serviceMessage(AbstractEndpoint.java:884)
at flex.messaging.endpoints.amf.MessageBrokerFilter.invoke(MessageBrokerFilter.java:121)
at flex.messaging.endpoints.amf.SessionFilter.invoke(SessionFilter.java:44)
at flex.messaging.endpoints.amf.BatchProcessFilter.invoke(BatchProcessFilter.java:67)
at flex.messaging.endpoints.amf.SerializationFilter.invoke(SerializationFilter.java:146)
at flex.messaging.endpoints.BaseHTTPEndpoint.service(BaseHTTPEndpoint.java:278)
at flex.messaging.MessageBrokerServlet.service(MessageBrokerServlet.java:322)


Thanks

Abhishek

Master Domaindi said...

Useful information shared. I am extremely pleased to read this write-up, it is juat a marvelous posting.
I seriously enjoyed reading it, you’re a great author.I will ensure that I bookmark your blog and will eventually come back at some point. I want to encourage you continue your great work, have a nice morning, afternoon or evening! Thanks again for giving us nice info and concise article.