Putting it all together: Mobile application services and Dojo

Using Web 2.0 and Mobile application service building blocks with Dojo to create a mobile client application

The latest version of the IBM® WebSphere® Application Server Feature Pack for Web 2.0 and Mobile features dojox.mobile components and mobile application service server-side building blocks. These building blocks greatly simplify the development of web applications targeted for smartphone client devices such as iPhone and Android. The application service building blocks enable server-side features that implement the protocols defined by some specific Dojo widgets. This article shows how to fit all of these pieces together into one cohesive, useful web-based application. This content is part of the IBM WebSphere Developer Technical Journal.

Michael Rheinheimer (rott@us.ibm.com), Advisory Software Engineer, IBM

Author photoMichael Rheinheimer is a 10-year veteran at IBM. He has worked extensively in web-based technologies and specifications, including JAX-RPC and JAX-WS web services, and JAX-RS REST. He has contributed to many open source projects, is a committer at Apache Software Foundation, and participates in Dojo Toolkit JavaScript framework. His latest endeavor is forging new paths and enabling ease of adoption of Web-based mobile application client technologies. Contact Michael at rott@us.ibm.com.



22 June 2011

Also available in Chinese

Introduction

The IBM WebSphere Application Server Feature Pack for Web 2.0 and Mobile (hereafter referred to as the Web 2.0 and Mobile feature pack) extends the reach of enterprise applications from the desktop to mobile devices. Version 1.1 includes application service building blocks that can greatly simplify integration of the client function to server capability.

By stepping through the high level tasks for creating a sample mobile application, this article will demonstrate how to use the application service building blocks in the feature pack to build up a Dojo-based web application client that provides a fast and meaningful user experience.

Before you begin

It is not necessary to download and install the following components to understand the principles outlined in this article, but you will certainly be more knowledgeable if you do.

To follow along in your own lab environment, you need access to IBM WebSphere Application Server V7 or greater (or another Java EE application server) with the IBM WebSphere Application Server Feature Pack for Web 2.0 and Mobile V1.1 properly installed and operational. The Web 2.0 and Mobile feature pack provides several application service building blocks and the Dojo Toolkit, which make up all the pieces you need to complete this project.

When you have the Web 2.0 and Mobile feature pack installed on WebSphere Application Server, you will find these resources (which are described in later sections):

  • WEBSPHERE_APP_SERVER_ROOT/installableApps/application_services/graphics/appsvcs-graphics.ear
  • WEBSPHERE_APP_SERVER_ROOT/installableApps/application_services/analytics/appsvcs-analytics.ear
  • WEBSPHERE_APP_SERVER_ROOT/installableApps/application_services/optimizer/appsvcs-optimizer.ear
  • WEBSPHERE_APP_SERVER_ROOT/samples/application_services/fileuploader/appsvcs-fileuploader.ear
  • WEBSPHERE_APP_SERVER_ROOT/samples/application-services/directorylisting/appsvcs-directorylisting.ear

Each of these applications is an installable enterprise application archive (EAR). However, some have configuration parameters you must edit to tie things together. The steps for configuring and installing each EAR file is the same for all five, so the process is described here once for easy reference.

Configuring application options

An EAR file can contain one or more web application archive (WAR) files. In this case, each EAR has one WAR. IBM uses a REST style approach for the implementation of the server-side applications; in this case, the REST implementation is a JAX-RS implementation. Each WAR file contains a web.xml servlet definition file that has the typical servlet definitions, including some custom init-params to configure options specific to the application contained within the WAR.

Specifically, the java.io.tmpdir parameter value must be changed. Using java.io.tmpdir as a special value in the param-value instructs the underlying application service to use the special servlet container value for key javax.servlet.context.tempdir. Changing this value in the web.xml file does not affect the JVM or servlet values.

To edit the init-params in each WAR file (using FileUploader as the example):

  1. Unzip the appsvcs-fileuploader.ear file and locate appsvcs-fileuploader-war-1.0.0.0.war.
  2. Unzip the appsvcs-fileuploader-war-1.0.0.0.war file and locate the WEB-INF/web.xml file.
  3. Edit the WEB-INF/web.xml file. Find the <param-value>java.io.tmpdir</param-value> and change the value of java.io.tmpdir to the desired value.
  4. Compress (zip) the WEB-INF/web.xml file back into the appsvcs-fileuploader-war-1.0.0.0.war file.
  5. Compress the appsvcs-fileuploader-war-1.0.0.0.war file back into the appsvcs-fileuploader.ear file.

Understand that the intent of this article is to show how to tie Dojo to server-side application services. As such, concepts around JavaScript™ and Dojo programming are mentioned at a high level. It is assumed that you have some basic knowledge of JavaScript programming.

Installing the applications

Many Java™ EE application servers have a folder to which you may drop an EAR file for "hot deployment." You can use such a feature if you wish, or you can install the EAR file via installation scripts, or use the administrative console. Regardless of how you choose to install each EAR, you should accept all defaults when prompted, and confirm that the installation was successful and that the application is running. Ensuring the application is running is a separate step in some application servers, including WebSphere Application Server.

Creating the client application

Because the simple applications you are creating here as examples require only minimal edits, a sophisticated IDE is not necessary for the purposes of this article. For actual mobile application development, consider using IBM Rational® Application Developer, which has all of the components you need to quickly build up a sophisticated server-side application.

You might ask, "where is the server?" It's a good question, but before we answer that you must first gather all the pieces together that enable quick, live creation and development of the client. It's often easiest to edit HTML and JavaScript on a live test server so you can test your changes immediately. This is especially useful when testing how the Dojo JavaScript client code interacts with the server-side application services. First, you create the dojox.mobile skeleton, then incrementally add to it as you configure and install the application building blocks.

You need to create an HTML file and collect the Dojo Toolkit to be placed on the same application server to which the application building block EAR files will later be installed. You'll do this in a new WAR file. There are excellent IBM products that enable building up large-scale client applications, but for this simple example it’s fine to use basic utilities.

  1. Create this directory structure in a temporary directory:
    • WEB-INF
    • dojo
  2. Create the web.xml file shown in Listing 1 under the WEB-INF directory so you can serve up static file content from the WAR. You might have a web server or some other preferred means of delivering static file content, but it is typically easiest to place the static content WAR on the same server as the application building blocks with which you'll be interacting due to the browser's same origin policy.
    Listing 1
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app id="My_Dojo_App" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
      http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
        <display-name>Dojo Client Application</display-name>
    
        <welcome-file-list>
            <welcome-file>index.html</welcome-file>
        </welcome-file-list>
    </web-app>
  3. Copy all of the contents from WEBSPHERE_APP_SERVER_ROOT/web2mobilefep_1.1/ajax-rt_1.X/ to the dojo directory. Your Dojo client application will not be using all of the components provided from this directory, but it doesn’t hurt to have it all packaged in this WAR; Dojo downloads only what it needs based on the requires statements in the JavaScript code. In production, you would create an optimized Dojo build anyway (see Resources).
  4. Create a boilerplate index.html file with the file shown in Listing 2.
    Listing 2
    <!DOCTYPE html>
    <html lang="en">
    <head>
    
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,
            initial-scale=1,
            maximum-scale=1,
            minimum-scale=1,
            user-scalable=no"/>
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <title>My Application Name</title>
    
    <script
            type="text/javascript"
            djConfig="parseOnLoad: true"
            src="dojo/dojo/dojo.js">
    </script>
    
    <script type="text/javascript">
    // your Dojo-based application code goes here
    </script>
    
    </head>
    
    <body>
    </body>
    </html>
  5. Zip up the dojo directory, WEB-INF directory, and index.html file into a WAR file called MyDojoClientApplication.war. Install this WAR file to your application server. As you install it, the server might prompt you for a context root. If so, enter a value such as myclient. As a result, the index.html file should be accessible at http://<SERVER>:<PORT>/myclient/index.html.

Now you are ready to install some application services, and tie the Dojo client into them.

Integrating the first service: Analytics

No special server-side configuration is required for the Analytics service. Find the WEBSPHERE_APP_SERVER_ROOT/installableApps/application_services/analytics/appsvcs-analytics.ear file and install it to your application server. Edit the index.html file so that the code shown in Listing 3 is changed to the code shown in Listing 4.

Listing 3
<script
        type="text/javascript"
        djConfig="parseOnLoad: true"
        src="dojo/dojo/dojo.js">
</script>
Listing 4
<script
        type="text/javascript"
        djConfig="parseOnLoad: true,analyticsUrl: '/appsvcs-analytics/rest/analytics/logger'"
        src="dojo/dojo/dojo.js">
</script>

Also, add the lines shown in Listing 5 to the so-far empty script tag with the your Dojo-based application code goes here code comment.

Listing 5
// analytics
dojo.require("dojox.analytics.plugins.dojo");

Use the Asynchronous Modules format in applications for declaring required components rather than the traditional dojo.require statement . Newer versions of Dojo, beginning with 1.7, support the Asynchronous Modules format, and some performance gains can be achieved by taking advantage of this support (see Resources).

Refresh the index.html file in your web browser and you’ll see that the Analytics service is reporting to the application server's default logs. (There are additional configuration options and features in the analytics service and client, such as logging client events, that are beyond the scope of this article.)

Integrating the second service: DirectoryListing

The directory listing service implements the protocol specified by dojox.data.FileStore, but the techniques used here could apply to many different situations that would benefit from lazy-loading data.

  1. As instructed earlier, the first thing to do is configure the DirectoryListing service. This means you'll want to unzip the DirectoryListing EAR file, unzip the WAR file, edit the web.xml to point to an existing directory of your choice, zip it all up again, and install it. Once this is all done, you have some client code to write.
  2. Tell Dojo all the modules you'll need. Add the dojo.require statements under the dojo.require call for analytics (Listing 6).
    Listing 6
    // mobile bootstrap
    dojo.require("dojox.mobile.parser");
    dojo.require("dojox.mobile");
    dojo.requireIf(!dojo.isWebKit, "dojox.mobile.compat");
    dojo.require("dojox.mobile.deviceTheme");
    
    // directorylisting
    dojo.require("dojox.data.FileStore");
    dojo.require("dojox.mobile.RoundRectDataList");
    
    // app
    dojo.require("dojo.data.ItemFileReadStore");
    dojo.require("dojo.NodeList-traverse");

    Notice the application being developed is targeted for smart phones. Hence the dojox.mobile.* requirements. You are building up an application that communicates with the DirectoryListing service, lazy-loading subdirectories as needed, so you will need to create dojox.mobile.Views with dojox.mobile.RoundRectDataLists tied to the dojox.data.FileStore, which communicates to the server-side DirectoryListing service.
  3. Create and render an initial dojox.mobile.View. Create your application definition with the dojo.require statements, shown in Listing 7.
    Listing 7
    var MyApp = {
    
        // Create a new view
        // id - the element id to give to the newly created node
        createView: function(id) {
    
            var view = new dojox.mobile.View({
                id: id,
                selected: true
            }, dojo.create("DIV", null, dojo.body()));
            view.startup();
    
            var headingArgs = {
                label: "Dynamic View"
            }
    
            var heading = new dojox.mobile.Heading(headingArgs);
        
            view.addChild(heading);
    
        },
    
        loadInitialList: function() {
            this.createView('view1');
        }
    
    };
    
    dojo.addOnLoad(function() {
        MyApp.createView("view1");
    });
  4. You've created a view, but it does not show a list of the files from the DirectoryListing service because you have not connected anything to it. You now need to supply the dojox.data.FileStore object, with the right properties, to the createView function so it can populate the created View with a RoundRectDataList. Notice the added parameters to createView in Listing 8, and that you call loadInitialList instead of createView in the onLoad event.
    Listing 8
    var MyApp = {
    
        // Create a new view if necessary and make transition
        // li - the list item node that was clicked
        // id - the element id to give to the newly created node
        // store - the *store object you will pass to the RoundRectDataList
        createView: function(id, store) {
    
            var view = new dojox.mobile.View({
                id: id,
                selected: true
            }, dojo.create("DIV", null, dojo.body()));
            view.startup();
    
            var headingArgs = {
                label: "Dynamic View"
            }
    
            var heading = new dojox.mobile.Heading(headingArgs);
        
            view.addChild(heading);
    
            var items = [];
            var scope = this;
    
            // you need to do some manipulation of the items so you can pass them in
            // a store to the new RoundRectDataList you'll be creating in onComplete
            store.fetch({
                onItem: dojo.hitch(scope, function(item, request) {
                    var path = item.path;
                    var name = item.name;
                    var targetId = path.replace('/', '_') + '_' + name;
                    if (item.directory == true) {
                        // this item is a directory, so you enable
                        // clicking on it to traverse into its contents
                        item.moveTo = '#';
                        item.transition = "slide";
                        item.id = targetId;
                        item.onclick = function(){alert("clicked!")};
                    }
                    items[items.length] = item;
                }),
                onComplete: function() {
                    var list = new dojox.mobile.RoundRectDataList({
                        label: 'name',
                        store: new dojo.data.ItemFileReadStore({
                            data: {label: 'name', items: items}
                        }),
                    });
                    view.addChild(list);
                    view.show();
                }
            });
        },
    
    
        loadInitialList: function() {
        
            var store = new dojox.data.FileStore({
                url: "rest/directorylisting"
            });
    
            this.createView('view1', store);
        }
    
    };
    
    dojo.addOnLoad(function() {
        MyApp.loadInitialList();
    });
  5. Now you can see the top level of files and directories from the DirectoryListing service. You want to be able to traverse down through the hierarchy though, so you must register an event listener to the click (or touch) of the items in the list that are directories. So instead of alert("clicked!"), you should call another function. Change alert("clicked!") to scope.handleClick(item) and add the handleClick function to MyApp (Listing 9).
    Listing 9
        handleClick: function(item) {
    
            // show animated gif icon to indicate background activity
            var prog = dojox.mobile.ProgressIndicator.getInstance();
            dojo.body().appendChild(prog.domNode);
            prog.start();
    
            // internal function to be called once all of the child items are loaded
            var triggerView = dojo.hitch(this, function(arrOfItems) {
                var store = new dojo.data.ItemFileReadStore({
                    data: {label: 'name', items: arrOfItems}
                });
                // find the dom node that was clicked
                var li = dojo.byId(item.id[0]);
                // stop the animated gif icon
                prog.stop();
                this.createView(li, 'view' + li.id, store);
            })
    
            dojo.xhrGet({
                url: "rest/directorylisting" + item.path[0],
                handleAs: "json",
                load: function(data) {
                    var dataLength = data.children.length;
                    var items = [];
                    dojo.forEach(data.children, dojo.hitch(this, function(item, i) {
                        dojo.xhrGet({
                            url: this.url + "/" + item,
                            handleAs: "json",
                            load: function(subdata) {
                                items[items.length] = subdata;
                                if (dataLength == items.length) {
                                    triggerView(items);
                                }
                            },
                        });
                    }));
                },
            });
        },
  6. If you try it now, you'll see that it does not behave as you wanted. The View created upon clicking an item appears below the existing View and does not have a list in it. This is due to the lack of transition from the existing View to the new View, and incorrectly processing the store data. The dojox.data.FileStore holds items differently than dojo.data.ItemFileReadStore, which wraps item properties in arrays for efficiency. Therefore, you have two new requirements to manage; createView must know from what view it must transition, and what type of store was passed to it. Replace the createView function with the new version (Listing 10).
    Listing 10
        // Create a new view if necessary and make transition
        // li - the list item node that was clicked
        // id - the element id to give to the newly created node
        // store - the *store object you will pass to the RoundRectDataList
        createView: function(li, id, store) {
    
            if(dijit.byId(id)) {
                // target view already exists, just transition to it
                dijit.byNode(li).transitionTo(id);
                return;
            } else {
                var view = new dojox.mobile.View({
                    id: id,
                    selected: true
                }, dojo.create("DIV", null, dojo.body()));
                view.startup();
    
                var headingArgs = {
                    label: "Dynamic View"
                }
                if (li) {
                    headingArgs.back = "Back";
                    // find the parent View's id so you can enable the back button
                    var ulParent = dojo.query('#'+li.id).parent();
                    var divParent = dojo.query('#'+ulParent[0].id).parent();
                    headingArgs.moveTo = divParent[0].id;
                };
    
                var heading = new dojox.mobile.Heading(headingArgs);
            
                view.addChild(heading);
    
                var items = [];
                var scope = this;
    
                // you need to do some manipulation of the items so you can pass them in
                // a store to the new RoundRectDataList you'll be creating in onComplete
                store.fetch({
                    onItem: dojo.hitch(scope, function(item, request) {
                        // if we're fetching from dojox.data.FileStore, the item
                        // properties are *not* wrapped in an array like they are
                        // with ItemFile*Store
                        var path;
                        var name;
                        if (item.path instanceof Array) {
                            path = item.path[0];
                            name = item.name[0];
                        } else {
                            path = item.path;
                            name = item.name;
                        }
                        var targetId = path.replace('/', '_') + '_' + name;
                        if ((item.directory == true) ||
                                ((item.directory instanceof Array)
                                  && (item.directory[0] == true))) {
                            // this item is a directory, so you enable
                            // clicking on it to traverse into its contents
                            item.moveTo = '#';
                            item.transition = "slide";
                            item.id = targetId;
                            item.onclick = function(){scope.handleClick(item)};
                        }
                        items[items.length] = item;
                    }),
                    onComplete: function() {
                        var list = new dojox.mobile.RoundRectDataList({
                            label: 'name',
                            store: new dojo.data.ItemFileReadStore({
                                data: {label: 'name', items: items}
                            })
                        });
                        view.addChild(list);
                        if (li) { // you got here from a click
                            dijit.byNode(li).transitionTo(id);
                        } else {
                            view.show();
                        }
                    }
                });
            }
        },
  7. Also, now that createView takes three parameters, you must change the calls to createView in both the loadList function and the handleClick function to pass the appropriate parameters:
    • In handleClick, change this.createView('view' + li.id, store); to this.createView(li, 'view' + li.id, store);.
    • In loadInitialList, change this.createView('view1', store); to this.createView(undefined, 'view1', store);.

Directory listing integration is now complete. You can now traverse through the hierarchy of files in a lazy-loading way from the DirectoryListing application service.

As an enhancement to your application, you could certainly attach onclick events to the non-directory items to show information about these files, or if they are accessible on your web server, to show the actual file contents.

A file is included with this article for download that contains the web.xml and index.html file as a starting point for demonstrating the client application proposed in this section. The included WAR file is provided as an example of the recommended format for structuring Dojo applications as of Dojo version 1.7.

Integrating the third service: FileUploader

The FileUploader application service sample receives a standard HTML multipart/form-data submission in a JAX-RS resource class. Most of the heavy lifting of parsing the inbound message payload is already done by the time the FileUploader method is called with the submitted data. The Java source code is for this example is provided for you to study.

Because this article deals specifically with integrating these services into a dojox.mobile client, and because only a select few smart phones at the time of this writing enable filesystem browsing from within the mobile web browser, integration of this service with the sample application you've created so far is not shown. You can review the Java and Dojo source code for this sample service if you wish.

Integrating the fourth service: Graphics

The Graphics application service enables on-the-fly image size reduction, among other features. It will also convert the HTML5 SVG tag contents into one of several wanted image formats. Each of these two features are useful in a mobile environment. For example, if you host a photo gallery site with images having large dimensions intended for viewing on a desktop monitor, you may want to reduce the image dimensions based on the capabilities of the client device visiting your site. This saves on network latency, greatly reduces load time (especially on cell phone networks), and improves the client experience by making the application more responsive.

One way to integrate the Graphics application service is to detect the client browser window dimensions and pass it, along with the image URL, to the Graphics service, so that the client is never attempting to download an inline HTML image that extends beyond the pixel dimensions of the smart phone's browser real estate.

For example, you might create all of the img tags dynamically such that all of the URLs in the src attributes are filtered through the Graphics service. In Dojo, this is only a few lines of code. First, include <div id="gallery"></div> within the opening and closing body tags, and then write the Dojo code to populate the IMG tags (Listing 11).

Listing 11
<script type="text/javascript" src="dojo/dojo/dojo.js"></script>

<script type="text/javascript">
dojo.require("dojo.window");

var MyImageURLs = [
    "/image1.gif",
    "/image2.jpg",
    "/image3.png"
];

dojo.addOnLoad(function() {
    
    var maxWidth = dojo.window.getBox().w;
    var maxHeight = dojo.window.getBox().h;
    dojo.forEach(MyImageURLs, function(url, i) {
        // use the graphics service to convert all images to jpg and
        // limit their dimensions to the size of the browser window
        var imgTag = dojo.create('img', {
            src:"/appsvcs-graphics/rest/graphics/convert/binaryResponse?sourceUrl="
                + url
                + "&desiredFormat=jpg&maxWidth=" + maxWidth + "&maxHeight=" + maxHeight
        });
        dojo.place(imgTag, dojo.byId("gallery"), "last");
    });
});
</script>

Integrating the fifth service: Optimizer

The release notes and documentation for the feature pack might refer to this service as Browser Detection service, but it is better described as a file optimizer service. It has many configuration options to manipulate the HTTP cache headers. A typical web browser will cache a file for a fixed amount of time, as directed by the Cache-Control or Expires headers in the server response. The client might send a request for a cached file to the server only to receive a 304 Not Modified reply. If, for example, you want to prevent even this network traffic for your smart phone clients, you might set com.ibm.ws.mobile.appsvcs.optimizer.cacheDeltaMin to the number of seconds you want to quell network traffic. If the typical duration of use of your application is five minutes, it might make sense to set this value to 300.

Besides the fine tuning you can do with cache headers, this service also gzips files returned to the client, if the client is capable of receiving and processing gzip content. This is all seamless to the client; no configuration or special code is required to enable this feature in the service. For application servers that do not automatically gzip static file content, this service solves this problem.

To use this service, you would first edit the configuration in the web.xml file of the Optimizer service to point to the root directory of the files you intend to serve through it. You would then install it and redirect the URLs in your HTML to retrieve content from the Optimizer service. Listing 12 shows an example.

Listing 12
<script
        type="text/javascript"
        djConfig="parseOnLoad: true,
            analyticsUrl: '/appsvcs-analytics/rest/analytics/logger'"
        src="/appsvcs-optimizer/rest/optimizer/dojo/dojo/dojo.js">
</script>

Notice the URL in the src attribute of the script tag. The dojo.js and all subsequent files downloaded due to dojo.require statements will be downloaded via the Optimizer service. The client web browser will honor the cache configuration you've defined and will receive gzipped files if it is capable. This improves performance over the network, and generally enhance the client user experience by making the application more responsive by limiting network traffic.

The Dojo build

Application development is now complete, except for one final task. The last step before deploying your application into the production environment is to produce a Dojo build. Creating a Dojo build aggregates many of the modules into one JavaScript file, as well as any required cascading style sheets (CSS), and minifies them. This greatly reduces the number of files downloaded by the client application as well as the overall size of the downloaded content. The initial load time for the application is reduced to fractions of what would be observed if you deployed with an un-built Dojo application.

See Resources for information describing how to perform the Dojo build.

Conclusion

Integrating application services into your mobile client application can greatly enhance the overall user experience with significantly improved performance. If done properly, the combination of the Dojo build, Optimizer service, Graphics service, and the lazy-loading techniques built into the DirectoryListing service and the corresponding dojox.data.FileStore will make a fast-performing, responsive client application. Adding features such as FileUploader and Analytics makes the application that much more functional and serviceable. Put them all together with the rest of your application and you can have a fast, feature-rich, serviceable application up and running quickly!


Download

DescriptionNameSize
Code sample1106_rheinheimer-mobile-dojo.war3 KB

Resources

Learn

Get products and technologies

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere, Agile transformation
ArticleID=681618
ArticleTitle=Putting it all together: Mobile application services and Dojo
publish-date=06222011