Ajax - [펌] AJAX and Web Service with WSO2
목록  
제 목 [펌] AJAX and Web Service with WSO2
작성자 박세청 작성일 2007/08/08 13:00


AJAX and Web Services with WSO2


August 2007
Discussion

Creating the server side of an AJAX solution

The Web has become the dominant way to deploy applications. Web applications have numerous advantages over traditional desktop applications. However, the user interface of the application is one area that Web applications have traditionally been weak in comparison to their desktop brethren. The Web has progressed and rich Internet applications (RIAs) offer user interfaces that are competitive with desktop applications. There are many powerful technologies that can be used to create RIAs, but the most popular is certainly Asynchronous JavaScript and XML (AJAX.) This article shows you how AJAX can be used in conjunction with Web Services and how the WSO2 technology stack provides a complete solution for creating Web applications using AJAX and Web services.

Prerequisites

This article uses the WSO2 Web Services Application Server(WSAS) Chad sample. It is recommended that you download and install WSO2 WSAS 2.0. The article uses the servlet edition installed on Apache Tomcat. Any application server can be used with the servlet version, just follow the installation instructions included with WSO2. WSO2 requires Java 1.4 or 1.5 but there are no other prerequisites for it.

The article makes heavy use of Web services, and the reader should be familiar with SOAP and WSDL. There is also a lot of JavaScript and XSLT used. Familiarity with these technologies will be advantageous.

The AJAX Philosophy

Traditional Web applications have long used server side technologies such as Java to dynamically create static content on the server side that is then sent to clients for display. This leads to very coarse conversations between servers and clients. AJAX makes it much easier to send smaller, discreet messages between clients and servers. This has lead to a change in how user interfaces are created.

AJAX clients are much smarter than traditional clients. They don’t just display static content from the server, they create they dynamically create the content themselves. It is common practice in AJAX systems for clients to request data or invoke services from the server and then dynamically update the user interface themselves.

There are a couple of keys to this kind of system. First, the client has to be able to process data from the server. The data can be sent in any format, but as the X in AJAX indicates, XML is a common and natural format. JavaScript is also a natural choice for processing the XML based data, but it’s not the only way. Modern browsers also support eXtensible Stylesheet Language Transformations (XSLT). XSLT can be easily used to transform XML to HTML for display in a Web browser. Indeed, this was often done on the server side in many older Web applications.

Once the data from an AJAX call is processed, it must be used to change the user interface. The key to this is Dynamic HTML (DHTML) and JavaScript. JavaScript allows for a Web page to be accessed and modified using the XML Document Object Model (DOM.) This leads to a natural flow: an AJAX request is made, data in its response is processed into new UI elements, and the DOM is modified to incorporate the new UI elements (usually either an insert or an update of the DOM.)

It’s common these days for the client side to receive most of the attention in AJAX applications. It’s only half of the equation though, and it’s easy to forget all the processing that must be done on the server side of an AJAX application. AJAX allows for some simplifications on the server side, as much of the presentation logic is moved to the client side. There are some complications though, as the server must still serialize data for use by the view logic on the client side.

As mentioned earlier, one natural way to serialize the data being sent to the client is using XML. Thus AJAX applications need to invoke services that produce XML as their response. This can be done using traditional view technologies such as JavaServer Pages or PHP to create the XML, but any technology for creating XML such as DOM or StAX or even JAXB can be used instead.

AJAX with Web Services

If you are a Web service developer, then this whole story of creating and consuming services using XML has to sound very familiar. It’s exactly how Web services work. So could it be that Web services can be leveraged to easily create AJAX Web applications? Indeed this is the case. Of course most Web services use SOAP, so the question really becomes: Can AJAX work with SOAP?

The answer is a resounding yes. AJAX clients make HTTP requests, just like a typical SOAP request. They can make HTTP Posts and send an XML document in the body of the request, just like a SOAP request. They can set headers in the request, such SOAPAction header commonly used in SOAP. AJAX response handlers commonly process XML using the responseXML property of the XMLHttpRequest object. Thus they can easily accept XML, such as SOAP responses.

Thus there are only a few things we need in order to start using Web services and AJAX as a complete Web application stack. First, we need a way to create SOAP requests on the client using JavaScript. Next, we need to be able to expose server side services as Web services. Luckily there’s a single solution to both of these problems: The WSO2 Web Srvices Framework(WSF). Let’s take a look at an example of how WSO2 WSAS leverages the power of Web services to create a complete AJAX solution.

The WSO2 WSAS Chad Sample

WSO2 WSAS ships with several useful and informative samples. Let’s take a look at the Chad sample. This is a Web application for creating and administering polls. It provides two sets of functionality. The basic user functionality allows you to view various polls and then take those polls. Of course, somebody has to create those polls, and that is done through the administrator role. The administrator can creates polls and manage their lifecycle. For more information on installing and building the Chad sample, see the resources section.

The Chad sample is a complete Web application. It includes a back-end set of server components exposed as a Web service. It also includes a front-end web application with an AJAX enhanced user interface. Take a look at the Chad source code found is $WSO2_HOME/samples/Chad. It contains a directory called www. Everything in this directory will be deployed as part of the Axis2 service Archive (.aar file) and will be the UI for that Web application. For the Chad sample, there is only one Web page, index.html, and it is included at the top level of the www directory. We only need one page, as all updates will be done using AJAX.

The Server Side of the Chad Sample

Let’s take a look at the code in the Chad sample. The class org.wso2.wsas.sample.chad.Chad contains all the business methods we need for the application. For example, Listing 1 shows the listPolls() method which shows all the active polls.

Listing 1. The listPolls method in the Chad class

public synchronized ChadPoll[] listPolls() {
        ChadPoll[] chadPolls = dataProvider.getAllChadPolls();
        for (int i = 0; i < chadPolls.length; i++) {
            getResult(chadPolls[i].getPollId());
        }
        return chadPolls;
    }

This method (as well as most of the other methods in the class) uses a classic Data Access Object (DAO) pattern for retrieving data from a data store. In this case, the class org.wso2.wsas.sample.chad.ChadDataProvider defines the interface for our DAO, which is eventually implemented using Hibernate to access the Apache Derby embedded database.

One of the cornerstones of WSO2 WSAS is Apache Axis2. Axis2 has been one of the most popular ways to create and consume Web services. Axis2 allows us to easily expose a POJO(Plain Old Java Object) like the Chad class as a Web service. We can use its Java2WSDL tool to generate a Web Services Description Language (WSDL) document that will tell our clients how to call our Web service and what to expect as the response. For example, Listing 2 shows an excerpt from the WSDL for the Chad Web Service, describing the listPolls Web method.

Listing 2. Chad Web Service WSDL

<wsdl:operation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" name="listPolls">
            <wsdl:input xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
                        xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" message="ns1:listPollsMessage"
                        wsaw:Action="urn:listPolls"/>
            <wsdl:output xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
                         message="ns1:listPollsResponse"/>
        </wsdl:operation>

WSO2 WSAS and Axis2 make it easy to take our back-end business logic and expose it as a Web service. The Chad sample provides a great example for creating an AJAX front end that leverages the power of Web services. Let’s walk through how Web services are invoked via AJAX in the Chad sample.

The Client Side of the Chad Sample: Making a SOAP Request

At the end of the day, a SOAP request is an HTTP request, just like any other AJAX request. The big difference between invoking a Web service using SOAP is that the request has to be formatted as a SOAP message. You’ve probably seen and maybe even written applications that use server side logic to create SOAP requests. This can also be done in JavaScript. There’s a lot of boilerplate code needed to format the request per the SOAP specification. This can be tedious, and there are numerous server-side libraries and frameworks available to help with this. Luckily WSO2 gives us a client-side library for doing the same thing called WSRequest.js.

In the Chad sample, a Chad JavaScript object is used for calling web services, as well as processing the response from the server. All the various web service calls go through a single JavaScript function on the Chad object called simple send. Let’s take a look at send in Listing 3.

Listing 3. The Chad.send() JavaScript function.

/*
This method always invoke asynchronously. Thus, onLoad method should be given.
*/
    send : function(action, xmlPayload, onLoad, onError) {

        try {
            this.onLoad = (onLoad) ? onLoad : this._defaultOnload;
            this.onError = (onError) ? onError : this._defaultError;

            this.req = new WSRequest();
            var _loader = this;
            this.req.onreadystatechange = function() {
                _loader._onReadyState.call(_loader);
            }
            this._options["action"] = action;
            this.req.open(this._options, this._chadServiceURL, true);
            this.req.send(xmlPayload);
        } catch(e) {
            wso2.wsf.Util.alertWarning("Errors encountered when connecting to the server. " +
                                       e.toString());

        }


    }

As you can see from Listing 3, the WSRequest object mentioned earlier does the bulk of the work. You are encouraged to look at the many thing that this object does. Let’s take a look at its send method in particular, shown in Listing 4.

Listing 4. The WSRequest.send JavaScript

WSRequest.prototype.send = function(payload) {
    if (arguments.length > 1) {
        return new Error("Invalid input argument");
    }

    var req = null;        // string to be sent

    if (this._optionSet["HTTPMethod"] != null)
        var method = this._optionSet["HTTPMethod"];
    else
        var method = "POST";

    this._soapVer = WSRequest.util._bindingVersion(this._optionSet);

    if (payload != null)
    {
        // seralize the dom to string
        var content = WSRequest.util._serializeToString(payload);
        if (content == false) {
            throw new Error("Invalid input argument");
        }

        // formulate the message envelope
        if (this._soapVer == 0) {
            req = WSRequest.util._buildHTTPpayload(this._optionSet, this._uri, content);
        } else {
            req = WSRequest.util._buildSOAPEnvelope(this._soapVer, this._optionSet, this._uri, content);
        }
    }

    // Note that we infer soapAction from the "action" parameter - also used for wsa:Action.
    //  WS-A recommends keeping these two items in sync.
    var soapAction = this._optionSet["action"];

    this._xmlhttp.open(method, this._uri, this._async);

    switch (this._soapVer) {
        case 1.1:
            soapAction = (soapAction == undefined ? '""' : '"' + soapAction + '"');
            this._xmlhttp.setRequestHeader("SOAPAction", soapAction);
            this._xmlhttp.setRequestHeader("Content-Type","text/xml; charset=UTF-8");
            break;
        case 1.2:
            this._xmlhttp.setRequestHeader("Content-Type","application/soap+xml;charset=UTF-8" +
            (soapAction == undefined ? "" : ";action=" + soapAction));
            break;
        case 0:
            if (this._optionSet["HTTPInputSerialization"] != null) {
                contentType = this._optionSet["HTTPInputSerialization"]
            } else {
                contentType = "application/xml";
            }
            this._xmlhttp.setRequestHeader("Content-Type", contentType);
            break;
    }

    if (this._async) {
        this._xmlhttp.onreadystatechange = WSRequest.util._bind(this._handleReadyState, this);
        this._xmlhttp.send(req);
    } else {
        // sync call
        this.readyState = 2;
        if (this.onreadystatechange != null)
            this.onreadystatechange();

        this._xmlhttp.send(req);

        this._processResult();
        if (this.error != null)
            throw (this.error);

        this.readyState = 4;
        if (this.onreadystatechange != null)
            this.onreadystatechange();
    }
}

This method does so much! It handles different versions of SOAP. It sets SOAP headers. It takes care of creating an XMLHttpRequest object that is appropriate for whatever flavor of browser being used. It basically let’s us concentrate on creating the part of the SOAP message that is unique to the particular call being made. This is what we would normally see inside the soapenv:Body tags in a SOAP message. That’s what you would pass in as the payload to WSRequest.send() or as xmlPayload to Chad.send(). In the Chad sample, the page initializes itself by calling the listAllPolls() method, shown in Listing 5.

Listing 5. Chad.listAllPolls() JavaScript function.

listAllPolls : function () {
        var body_xml = '<req:listPollsMessage xmlns:req="http://www.wso2.org/types"/>';
        this.send("listPolls", body_xml, listPollsAllCallback);
    }

This is the SOAP element that is the actual invocation of the listPolls() method shown earlier. Notice the last parameter to send() -- listPollsAllCallback. This is a JavaScript callback function, i.e. it is the name of the JavaScript function that will be called when the response from the server is (asynchronously) received. We’ll look at that next.

There’s one final thing worth mentioning before moving on to response processing. The WSRequest library used here can be used as a general purpose JavaScript library for calling web services. In this case the web service was also created using WSO2, but it didn’t have to be. That’s the whole point of web services after all -- they are independent of the technology used to implement them. Maybe you have an existing web service developed using .NET or gSOAP or whatever. You could still use WSRequest to communicate with that service via JavaScript.

The Client Side of the Chad Sample: Processing a SOAP Resonse

Let’s take a look at the callback function referenced above, listPollsAllCallback. It is shown in Listing 6.

Listing 6. The listPollsAllCallback JavaScript function

function listPollsAllCallback() {
    wso2.wsf.Util.callbackhelper(this.req.responseXML, "list_polls_all.xsl", document.getElementById("divChadListPolls"));
}

That’s pretty simple! Of course the reason it is so simple is that it is leveraging another JavaScript library provided by WSO2: wso2.wsf.Util. That can be found in /main_www/js/main.js. Let’s take a quick look at its callbackhelper function in Listing 7.

Listing 7. The wso2.wsf.Util.callbackhelper JavaScript function

/*
This function will be used as an xml to html
transformation helper in callback objects. Works only with wso2.wsf.WSRequest.
@param xml : XML document
@param xsltFile : XSLT file
@param objDiv  : Div that trasformation should be applied
@param doNotLoadDiv : flag that store the div in browser history
@param isAbsPath : If xsltFile is absolute, then isAbsPath should be true
*/
    callbackhelper : function(xml, xsltFile, objDiv, doNotLoadDiv, isAbsPath) {
        this.processXML(xml, xsltFile, objDiv, isAbsPath);
        if (!doNotLoadDiv) {
            this.showOnlyOneMain(objDiv);
        }

    }

This function delegates to another function called processXML(), so let’s take a look at that function in Listing 8.

Listing 8. The wso2.wsf.Util.processXML JavaScript function

/*
@parm xml : DOM document that needed to be transformed
@param xslFileName : XSLT file name. This could be foo.xsl, which is reside in /extensions/core/js
                     or bar/car/foo.xsl. If the later version is used, the isAbstPath should be true.
@param objDiv : Div object, the transformed fragment will be append to it.
@param isAbsPath : Used to indicate whether the usr provided is a absolute path.

*/
    processXML : function (xml, xslFileName, objDiv, isAbsPath) {
        var xsltHelperObj = new wso2.wsf.XSLTHelper();
        xsltHelperObj.transform(objDiv, xml, xslFileName, isAbsPath);
    }

This method delegates to a wso2.wsf.XSLTHelper JavaScript class. This is another part of the JavaScript library provided by WSO2. It gives us a browser independent way of loading an XSLT processor. So why do we want that?

The output of a SOAP request to a Web service will be a SOAP response. There’s nothing magical about such a response, it is just XML. There are many ways to process XML. It can be parsed as text. It can be processed using a DOM parser or a custom parse using a SAX API.

Whatever technique is used, the data from the XML document is used to manipulate the DOM of the HTML document being viewed. Thus you often see JavaScript used to create HTML elements and then insert those elements into the user interface. An alternative approach is to use XSLT.

XSLT takes in an XML document and can produce any kind of text output, including HTML. This is the technique used in the Chad sample. Each SOAP response type has a matching XSLT file. The XSLT file for the listAllPolls() call can be found in www/xslt/list_polls_all.xsl and is shown in Listing 9.

Listing 9. The XSLT for the listAllPolls() SOAP Response

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:key name="kkk" match="isStopped"  use="."/>

    <xsl:template match="*">
        <h2>Currently Active Polls</h2>
        <div id="formset">
        <form>
        <fieldset style="border:none;">
        <xsl:choose>
            <xsl:when test="return">
                <xsl:variable name="isStoppedCount" select="count(key('kkk','true'))"/>
                <xsl:variable name="availableStopped" select="count(return)"/>
                <xsl:choose>
                    <xsl:when test="$isStoppedCount=$availableStopped">
                        <div><h4>No Active Polls !</h4></div>
                    </xsl:when>
                    <xsl:otherwise>
                        <!-- Rest of the stuff goes here -->
                        <table class="styled">
                                <thead>
                                <tr>
                                    <th>Poll Title</th>
                                    <th>Description</th>
                                    <th>Action</th>
                                </tr>
                                </thead>
                                <tbody>
                                <xsl:for-each select="return">
                                    <xsl:sort select="title"/>
                                    <xsl:choose>
                                        <xsl:when test="isStopped='false'">
                                            <tr>
                                                <td>
                                                    <a>
                                                        <xsl:attribute name="href">#</xsl:attribute>
                                                        <xsl:attribute name="onClick">
							javascript:wso2.wsas.Chad.static.viewPollDetails
							('<xsl:value-of select="pollId"/>'); return false;
														</xsl:attribute>
                                                        <xsl:value-of select="title"/>
                                                    </a>
                                                </td>
                                                <td>
                                                    <xsl:value-of select="description"/>
                                                </td>
                                                <td>
                                                    <a>
                                                        <xsl:attribute name="href">#</xsl:attribute>
                                                        <xsl:attribute name="onClick">
							javascript:wso2.wsas.Chad.static.eligibleForVoting
							('<xsl:value-of select="pollId"/>'); return false;
													</xsl:attribute>
                                                        <xsl:text>Vote</xsl:text>
                                                    </a>
                                                </td>
                                            </tr>
                                        </xsl:when>
                                    </xsl:choose>
                                </xsl:for-each>
                                </tbody>
                            </table>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:when>
            <xsl:otherwise>
                <b><i>No polls present</i></b>
            </xsl:otherwise>

        </xsl:choose>
        </fieldset>
        </form>
        </div>
    </xsl:template>
</xsl:stylesheet>

XSL is a common way to create HTML from SOAP (or any kind of XML.) With WSO2, we have an easy, cross browser way of creating an XSLT processor to help us turn the response from a SOAP request into HTML that can be used to update our UI.

Summary

We’ve walked through a complete example demonstrating how Web services can be invoked directly from JavaScript clients and make it easy to create an AJAX style Web application using Web services. We’ve seen how easy WSO2 and Axis make it to expose back-end APIs as Web services that can be consumed easily by JavaScript and XSLT. Adding AJAX to an application is as simple as adding Web services, and WSO2 makes that incredibly easy. We’ve seen the power of the WSO2 JavaScript libraries and how trivial these libraries make it to both invoke web services and process the responses from web services.

Resources

About the Author

Michael Galpin is a software engineer at eBay and has been developing Web applications since 1998. He holds a degree in mathematics from the California Institute of Technology.


PRINTER FRIENDLY VERSION

Related LinksRelated LinksRelated Links
Ads by Google
아리아리 토종 멧돼지
청정지역에서 자란 토종 멧돼지. 멧돼지 머리, 내장육 등.

www.ariari.com/

CJ뉴트라 클로렐라
CJ클로렐라, 천연 광합성 배양, 체질개선, 회원가입시 50%할인.

www.cjnutra.com/

바퀴벌레 다잡는 곳 한코
냄새 없고 안전한 바퀴벌레 박멸약 맥스포스, 바퀴벌레 박멸기 등 판매

www.HANKO.kr

바퀴벌레 퇴치 세스코
바퀴벌레, 개미, 쥐 완전박멸. 정기 관리 서비스, 환불보증제, 온라인견적

www.cesco.co.kr

여드름치료 한의원 려
강남역. 여드름의 원인진단 및 체질개선을 통한 근본적인 치료.

www.ryeoclinic.com





이전글 Ajax POST 방식으로 전송하기.
다음글 Ajax용 자바 객체 직렬화

목록