How to Use SOAP Web Services with Java and Flex: Part 2 of 2

One of the real problems we found was that we had about 10 services which return some fairly complex objects. Some of these objects had other objects as member variables, so it made for quite a complex SOAP response. The problem is that the WSDL wizard will create a whole set of objects for each service, even if we know they are related, it treats the as totally independent of each other. This is certainly a safe way of working, but it's not terribly efficient.

For instance, several of our services returned objects that also contained a User object. This meant that we now had several User objects in our Flex code which looked identical, but were treated by the compiler as not being the same because they were in different packages.

Even without this duplicate code problem, we still didn't want to have the masses of code that the wizard generated for us, so we had a go at hand writing the web service code in our Flex project...and it was pleasingly simple.

public class BaseWebService
{
    protected var service:WebService;

    public function BaseWebService(serviceName:String)
    {
        setupTypes();

        service = new WebService();
        service.loadWSDL(ModelLocator.instance.baseWsUrl + "/" + serviceName + "?wsdl");
        service.addEventListener(FaultEvent.FAULT, soapFaultHandler, false, 0, false);
    }

    public function soapFaultHandler(event:FaultEvent):void
    {
        Alert.show(event.fault.faultString);
    }

    private function setupTypes():void
    {
        SchemaTypeRegistry.getInstance().registerCollectionClass(new QName("http://model.mynamespace","ArrayOfCountry"),myflex.model.ArrayOfCountry);
        SchemaTypeRegistry.getInstance().registerClass(new QName("http://model.mynamespace","Country"),myflex.model.Country);
        ...
        ...
    }
}

As you can see above, we implemented a base class to do most of the work. The interesting parts here are:

  • The setup of the service by loading a WSDL file over the network from a location property loaded in from elsewhere.
  • The default fault handler is set here whereas the success handler will be set by the subclass implementation
  • The setupTypes() method is where you need to know a bit about what your WSDL looks like as you map elements to individual Flex objects. So a "Country" element in the "http://model.mynamespace" namespace is mapped to my "myflex.model.Country" object in Flex

The CountryWebService is then a subclass of BaseWebService:

public class CountryWebService extends BaseWebService
{

    public function CountryWebService()
    {
        super("CountryService");
    }

    public function getCountries(resultHandler:Function):void
    {
        Operation(service.getCountries).addEventListener(ResultEvent.RESULT, resultHandler, false, 0, false);
        service.getCountries();
    }
    public function deleteCountry(id:Number, resultHandler:Function):void
    {
        Operation(service.remove).addEventListener(ResultEvent.RESULT, resultHandler, false, 0, false);
        service.remove(id);
    }
    ...
    ...
}

Each individual web service we define just extends BaseWebService, passes in the name of the service and then provided an implementation for each method on that service (only getCountries is shown here). The implementations just take a callback result handler and optionally some data to send along to the SOAP service in the request.

The clever bit of Flex black magic is in the way the "id" in "deleteCountry" gets passed to the web service. Because Flex knows from the WSDL that my "remove" operation on my "CountryService" only takes a single numerical value, it knows how to map the "id" that I pass into the seemingly very loosely defined "service.remove(id)" method.

You can't reliably do something like this if you wanted to pass in 3 different parameters.

service.remove(id, user, reason);

Instead, it would be best to have your service receive an object called something like "RemoveCountryBean" and have that bean have 3 named parameters. So you'd call the remove operation like this instead:

var removeBean:RemoveCountryBean = new RemoveCountryBean();
removeBean.id = id;
removeBean.user = user;
removeBean.reason = reason;
service.remove(removeBean);

Flex then has a well defined object to work with that it can more easily map to your WSDL, so this method works well. For this reason, all of our Java objects that we expose as services take JavaBean type objects as their single parameter rather than lots of simple String, Integer, etc... type parameters.

It's also worth mentioning what our Flex Country class looks like. It's essentially a plain object with no mention SOAP. And because this is an object that we have written ourselves and isn't auto generated, we can add to it and alter it to our hearts content.

public class Country
{
    public var id:Number;
    public var name:String;

    public function Country() {}
}

Now the clever bit is show you can easily bind direct from a SOAP call right onto an object like a DataGrid.

public function loadCountries():void {
   var service:CountryWebService = new CountryWebService();
   service.getCountries(resultHandler);
}

private function resultHandler(event:ResultEvent):void {
    myDataGrid.dataProvider = (event.result as ArrayOfCountry);
}

And that is pretty much it. The DataGrid will get the ArrayOfCountry back and use it as its dataProvider, automatically looking up the objects inside and showing us a nice table of Country objects with id and name.

An important aspect of web services that I've not touched on at all here is security. Obviously you'll want to do some kind of authentication when talking to your web service, and we did just that...but it's not as simple as it may first seem so I'll cover it in another blog entry another time.

This site uses cookies. Continue to use the site as normal if you are happy with this, or read more about cookies and how to manage them.

X