In Apache Axis2/C AXIOM is used as the basic object model to represent XML. AXIOM provide a DOM like API that allows to traverse and build the XML very easily.

Anyway in underneath, AXIOM is different from DOM, as it has used some techniques to optimize the parsing of the XML as suited specially for SOAP message processing in web services. For an example the SOAP processor can validate a SOAP message by reading only some parts of the SOAP header fields, and if it is not valid, they can completely skip processing the body part. And since AXIOM is designed to built from a stream of data retrieved from a transport, sometimes SOAP processors can validate the message without the need of reading the full stream.

Anyway there should be lot of application that needs this optimization in parsing XMLs. They can easily adapt AXIOM/C to their application. Here is an AXIOM/C tutorial that covers both parsing and building XMLs from AXIOM. In this post I’d like to mention a code that can be used to retrieve an AXIOM from a String (char buffer) which we call as deserialization.

    axiom_node_t* AXIS2_CALL
    deserialize_my_buffer (
        const axutil_env_t * env,
        char *buffer)
    {
        axiom_xml_reader_t *reader = NULL;
        axiom_stax_builder_t *builder = NULL;
        axiom_document_t *document = NULL;
        axiom_node_t *payload = NULL;

        reader = axiom_xml_reader_create_for_memory (env,
            buffer, axutil_strlen (buffer),
            AXIS2_UTF_8, AXIS2_XML_PARSER_TYPE_BUFFER);

        if (!reader)
        {
            return NULL;
        }

        builder = axiom_stax_builder_create (env, reader);

        if (!builder)
        {
            return NULL;
        }
        document = axiom_stax_builder_get_document (builder, env);
        if (!document)
        {
            AXIS2_LOG_ERROR (env->log, AXIS2_LOG_SI,
                    "Document is null for deserialization");
            return NULL;
        }

        payload = axiom_document_get_root_element (document, env);

        if (!payload)
        {
            AXIS2_LOG_ERROR (env->log, AXIS2_LOG_SI,
                    "Root element of the document is not found");
            return NULL;
        }
        axiom_document_build_all (document, env);

        axiom_stax_builder_free_self (builder, env);

        return payload;
    }

Regardless of the fact this piece of code is been used many time by Axis2 and application that uses Axis2, it has never been identified as a core AXIOM function. I think it is better we have this function as an alternative method to create an axiom.

axiom_node_t *AXIS2_CALL
axiom_node_create_from_buffer(const axutil_env_t *env, axis2_char_t *buffer);

I already suggested this in Axis2/C mailing list and hopefully it will be included from the next release.

Here when we create the axiom tree function from the character buffer, we used “axiom_xml_reader_create_for_memory” function. Anyway whenever transport read data stream from wire it always uses the “axiom_xml_reader_create_for_io” function.

    /**
     * This create an instance of axiom_xml_reader to
     * parse a xml document in a buffer. It takes a callback
     * function that takes a buffer and the size of the buffer
     * The user must implement a function that takes in buffer
     * and size and fill the buffer with specified size
     * with xml stream, parser will call this function to fill the
     * buffer on the fly while parsing.
     * @param env environment MUST NOT be NULL.
     * @param read_input_callback() callback function that fills
     * a char buffer.
     * @param close_input_callback() callback function that closes
     * the input stream.
     * @param ctx, context can be any data that needs to be passed
     * to the callback method.
     * @param encoding encoding scheme of the xml stream
     */
    AXIS2_EXTERN axiom_xml_reader_t *AXIS2_CALL
    axiom_xml_reader_create_for_io(
        const axutil_env_t * env,
        AXIS2_READ_INPUT_CALLBACK read_callback,
        AXIS2_CLOSE_INPUT_CALLBACK close_callback,
        void *ctx,
        const axis2_char_t * encoding);

As you may have noticed it requires us to implement a “read_callback” function. Here is an example function prototype to implement this callback.

    int AXIS2_CALL
    some_function(
            char *buffer,
            int size,
            void *ctx);

This function will be called by the parser as required to parse the XML read from some stream.

So if your application involves reading data from a stream you are always recommended to use this function (i.e. “axiom_xml_reader_create_for_io”) instead of “axiom_xml_read_create_for_buffer” to create the AXIOM model more effectively.

Few weeks back, I did a screencast on how to consume a web service using WSF/PHP demonstrating an application of US National Digital Weather Forecast Database. In that webservice there are operations like “GmlLatLonList” that retrieve a KML for a given longitude and latitude inside USA. I wrote a simple mashup that load this KML from the web service and set it as a layer in a map which is created using openlayers.

There I use the WSO2 Mashup Server to create the stub to invoke the weather forecasting web service. This stub can be easily used to invoke the service from a javascript in a HTML page.

You can visit the mashup from the Mashup Server community site mooshup.com from here, http://mooshup.com/services/Dimuthu/weatherMap/.

Here are the steps I used to create this mashup.

  1. First I wrote a javascript to wrap the web service. It has a function that can be used as a stub to invoke the web service operation just as a javascript function call. Here is the source of the javascript I wrote. It wraps the “GmlLatLonList” service operation in the “fetchWeatherInfo” function. I gave this javascript service the name “weatherMap”.
    this.serviceName = "weatherMap";
    this.scope = "application";
    this.documentation = "Visit the weather information from just a mouse click";
    
    fetchWeatherInfo.documentation = "Retrieve the weather information for the given inputs" ;
    fetchWeatherInfo.inputTypes = {"listLatLon" : "string",
                                   "startTime" : "string",
                                   "endTime" : "string"};
    fetchWeatherInfo.outputType = "xml";
    function fetchWeatherInfo(listLatLon, startTime, endTime){
    
        var url = "http://www.weather.gov/forecasts/xml/SOAP_server/ndfdXMLserver.php";
    
        // setting up the the WSRequest
        var request = new WSRequest();
    
        var options = new Array();
        options.useSOAP = 1.1;
        options.useWSA = false;
    
        // do the request
        request.open(options, url, false);
    
        var reqXml =
            <ns:GmlTimeSeries xmlns:ns="uri:DWMLgen">
                <listLatLon>{listLatLon}</listLatLon>
                <startTime>{startTime}</startTime>
                <endTime>{endTime}</endTime>
                <compType>Between</compType>
                <featureType>Ndfd_KmlPoint</featureType>
                <propertyName></propertyName>
            </ns:GmlTimeSeries>
    
        request.send(reqXml);
    
        return request.responseXML;
    }
  2. Deployed it in the WSO2 Mashup server. I downloaded and setup the mashup server locally. Then I just needed to put the script inside “scripts/dimuthu” directory. There is an online instance of mashup server called mooshup.com in which I later deployed my service. After deployed it in the mashup, it will generate the javascript stub for that service. You can view the generated stub by adding a “?stub” to the mashup url. Here is the stub for the “weatherMap” mashup deployed in the mooshup server, http://mooshup.com/services/Dimuthu/weatherMap?stub. Similarly you can view the try out page for the service from, http://mooshup.com/services/Dimuthu/weatherMap?tryit
  3. Wrote an HTML interface for the service. I used the openlayers javascript library to load google map in to my page. And added a custom control that handles the click. In the click handler, I just call the stub and get the response KML string back using a code similar to this.
    <!-- include the script for the stub -->
    <script type="text/javascript" src="../weatherMap?stub"></script>
    
    <script language="javascript">
    ...
    
    // the code inside the handler of the click control
    
       // preparing the stub call
       weatherMap.fetchWeatherInfo.onError = handleError;
       weatherMap.fetchWeatherInfo.callback = function(response) {
           if(response && response.firstChild.firstChild.nodeValue) {
    
               // handling kml document
               var kmlDoc = response.firstChild.firstChild.nodeValue;
    
               // the code to add the kml layer to the map and render it
               ....
             }
        };
       // preparing the startTime, endTIme, lat, lon values
       ...
    
       // doing the request call
       weatherMap.fetchWeatherInfo(lat + "," + lon, startTime, endTime);
    
    </script>

    The KML style icon will be shown in the map after adding the KML layer to the map. And we can render the KML style details which in this case contain the weather forecast data in detail in some other place easily.

So this way you can create a mashup using openlayers and the data retrieved from different web services. You can find another mashup that shows the twitter updates on a map in real time at here, http://mooshup.com/services/tyrell/TwitterMap/.


© 2007 Dimuthu’s Blog | iKon Wordpress Theme by Windows Vista Administration | Powered by Wordpress