 | Level: Intermediate Vijaykumar Palat (vpalat@us.ibm.com)IBM Software Services for WebSphere for IBM
04 Jun 2008 IBM® WebSphere® sMash offers a variety of ways to share information in
Web 2.0 applicatons. This article shows how you can build a Google Gadget from
scratch, publish it, and power it using WebSphere sMash. Along the way, you
will examine the gadget XML specification, use the WebSphere sMash flow model
and feed tools, and, ultimately, deploy the gadget to a Web page.
Introduction
IBM WebSphere sMash is a flexible and agile platform for developing and delivering Web
2.0 applications. With an easy to use programming model that includes dynamic
languages, WebSphere sMash provides an excellent environment for building RESTful
services to extend an SOA, enabling you to consume services in the way that makes the
most sense for your situational objectives. This article describes how to build a
Google Gadget, a lightweight module constructed out of HTML, JavaScript™ and XML, that can be placed in a multitude of Web pages that you can power with WebSphere sMash.
Anatomy of a gadget
Gadgets are simple HTML and JavaScript
mini-applications that provide a lightweight way for
you to
share your data with others. Written once, a gadget can be utilized in many
places, including iGoogle, Google Maps, Orkut, IBM WebSphere Portal, or in
virtually any Web page or desktop. Figure 1 shows the iGoogle Web page, a
customizeable, portal version of the Google home page, populated with
different Google Gadgets pulled from other sites such as Slashdot.org,
Fool.com, BBC News, and Kiplinger Personal Finance.
Figure 1. iGoogle Web page
A basic gadget can be constructed from HTML, XML and JavaScript. A gadget
uses the XML document to describe the HTML and JavaScript that will be
rendered on the gadget container, which is the set of functions that
enables a gadget to be displayed in the page. This article uses the iGoogle.com
page as the container, but you could use a product like WebSphere Portal or
snippets of code provided by Google to display the gadget on a typical Web
page. The place to start building a gadget is in the gadget specification file, an example of which is shown in Listing 1.
Listing 1. Gadget specification for a Google Gadget
<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="Gadget Sample"
title_url="http://www.ibm.com/developerworks/"
height="200"
author="Jay Palat"
author_email="vpalat@us.ibm.com"/>
<UserPref name="exampleStr" display_name="Name" required="true" />
<Content type="html">
<![CDATA[
Hello, world!]]>
</Content>
</Module> |
When you break it down, you can see that a gadget is a module that consists of three parts:
-
Module preferences
ModulePrefs (Listing 2) refer to the module or gadget itself,
providing the attributes for publication and subscription. The display
attributes include the title, title URL, gadget dimensions (height and
width), scrolling (if the gadget content exceeds the page limits), and
author information (name, e-mail address, and so on). If a module is going to use a special library, then this must also be included in the ModulePrefs.
Listing 2. Module preferences
<ModulePrefs title="Gadget Sample"
title_url="http://www.ibm.com/developerworks/"
height="200"
author="Jay Palat"
author_email="vpalat@us.ibm.com"/> |
Gadgets also support other features (not detailed in this article),
including locale support and compatibility requirements. Locale enables
gadgets to have specific content based on language and country, and
compatibility enables gadget authors to set rules requiring the gadget
environment to be running under certain conditions. For instance, a gadget might require a specific platform, browser, or plug-in in order to run.
-
User preferences
UserPref (Listing 3) enables you to set specific preferences for the
gadget. For example. you might want users to be able to change the appearance of the gadget. A user preference has several components. The name attribute is the name of preference itself; unless the display_name is set, the user will see the UserPref name value by default. UserPref supports multiple data types, including string (the default), bool, enum, hidden and list.
Listing 3. User preferences
<UserPref name="exampleStr" display_name="Name" required="true" />
<UserPref name="exampleStr2" display_name="Photo"
default_value="http://doc.examples.googlepages.com/Rowan-headshot.jpg"/>
<UserPref name="exampleBool" display_name="Show Photo?" datatype="bool"
default_value="true"/>
<UserPref name="exampleEnum" display_name="Color" default_value="Yellow"
datatype="enum" >
<EnumValue value="Red" />
<EnumValue value="Yellow" />
<EnumValue value="Blue" />
</UserPref> |
Users cannot view or edit hidden strings. If a required flag is set to true, then the gadget will not render until the required UserPref has been set.
-
Content
Content is the substance of your gadget. A gadget must declare the
type of content available (the options are HTML and URL). Gadgets of type HTML encapsulate the HTML and JavaScript necessary to make the gadget work in the XML document itself. The code is all self-contained within the gadget. For this reason, the CDATA tag is required for gadgets of content type HTML, as in:
<Content type="html">
<![CDATA[ HTML here... ]]>
Notice below that the HTML included with the gadget does not contain the
<html>, <head>, or <body> tags; these items are handled at
the gadget container level and should not be included in the gadget's HTML fragment:
<Content type="html">
<![CDATA[
Hello, world!]]>
</Content>
As with typical HTML pages, JavaScript can be included inline or via an external file. Google provides a set of JavaScript libraries to help out. The Core JavaScript libraries provide access to the user preferences that were created in UserPrefs section.
Listing 4. Using Core JavaScript library
<![CDATA[
<script type="text/javascript">
function getPrefs()
var prefs = new _IG_Prefs(__MODULE_ID__);
var name = prefs.getString("exampleStr");
var url = prefs.getBool("exampleStr2");
var bool = prefs.getBool("exampleBool");
var primaryColor = prefs.getString("exampleEnum");
... |
Google also provides libraries for dealing with content, including feeds,
images, and XML documents. Table 1 shows some examples from the Google Gadgets API Reference page.
Table 1. Select features available from the Google Gadgets Core JavaScript library
| Function | Description |
|---|
_IG_FetchContent(url, func, {refreshInterval:num})
| Fetches the content at url. When it's ready, calls func. Note that _IG_FetchContent() is asynchronous, meaning that it returns immediately and then calls the inner function later, when the fetch finishes. This means that you must put any dependent code inside the callback function, or inside functions that are called by the callback function. The optional refreshInterval parameter lets you override the default caching behavior by refreshing the cache every num seconds. |
_IG_FetchXmlContent(url, func, {refreshInterval:num})
| Fetches the XML content at url. When it's ready, calls func. The optional refreshInterval parameter lets you override the default caching behavior by refreshing the cache every num seconds. Also asynchronous. |
_IG_FetchFeedAsJSON(url, func, num_entries, get_summaries)
| Fetches the RSS / Atom feed content at url and returns it as a JSON object. When it's ready, calls func. Fetches the number of feed entries specified by num_entries (default is 3, possible range is 1-100), and optionally fetches entry summaries depending on the value of get_summaries (default is false). Also asynchronous. |
_IG_RegisterOnloadHandler(func)
| Event handler that gets called on load. Takes a single argument, which is a function that is called after the gadget loads. The passed function takes no arguments. |
_gel(id)
| A wrapper around the JavaScript document.getElementById() function. |
_args()
| Returns the URL parameter
key-value pairs from document.location as an associative array. So, for example,
&foo=bar&up_cats=meow
would be returned as
{"foo":"bar", "up_cats":"meow"}.
Here is an example of how you might
use it to fetch the language or country:
var lang = _args()["lang"];
if (typeof lang == "undefined") {
lang = "en";
}
|
_IG_GetImage(url, {refreshInterval:num})
| Returns the cached version of the image specified by url. This can improve performance in gadgets that have static images. For example:
var img = _IG_GetImage(my_image_url);
The optional refreshInterval parameter lets you override the default caching behavior by
refreshing the cache every num seconds. The functions _IG_GetImageUrl() and
_IG_GetCachedUrl() also give you access to cached content. (Also see Improving Gadget Performance.)
|
_IG_GetCachedUrl(url, {refreshInterval:num})
| Returns the proxy URL for the cached version of the file specified by url. Using cached content can improve gadget performance. The optional refreshInterval parameter lets you override the default caching behavior by refreshing the cache every num seconds. |
While many gadgets are produced simply using the HTML type, there are circumstances where it makes more sense to run the gadget from a server-based solution. For those cases, you can use the content type URL:
<Content type="url"
href="http://www/cgi-bin/example/gadgets/mystats.cgi" />
URL-based gadgets do all their processing and logic on the server side. They
are hosted via an iframe. As the gadget creator, you are responsible for making sure the iframe edges are hidden using CSS.
Building and publishing a gadget
Although you can use any simple text editor to create the basic XML
specification, this article uses the Google Gadgets Editor (Figure 2) for ease
of use. The Google Gadgets Editor is a basic text editor with syntax highlighting that also provides a preview mode to show how the gadget will look once it is deployed (Figure 3). The Google Gadgets Editor page provides samples of other available gadgets, as well as API examples.
Figure 2. Google Gadgets Editor: Edit mode
Figure 3. Google Gadgets Editor: Preview mode
(The preview function will not be used in this example, so set the value to false when working in the editor, but true when publishing.)
The Google Gadgets Editor offers the following commands:
-
New: Create a new file
-
Open...: Open an existing file from the Google hosting platform
-
Open from URL: Open a file from a remote URL
-
Save: Save the file
-
Save As: Save with a new name
-
Rename: Rename an existing file
-
Upload: Upload a file to edit
-
Publish: Publish the gadget
Publishing a gadget places the content in publicly available space.
Publishing to Google enables other developers to take advantage of the Google
infrastructure in their development, including caching and high availability
of services. That said, you can publish your gadget to any Web site and
simply point your gadget container page to the URL of your gadget.
In the next sections, you
will see how to create content based on feeds using WebSphere sMash's flow
models and feed tools, and then how to power the gadget using WebSphere sMash.
Consuming a simple flow
WebSphere sMash provides the ability to aggregate feeds and coordinate
services together using an Assemble component. The feed aggregation feature is used here to take data from existing feeds and power a gadget that will consume the data.
A flow is composed of the following components and functions:
-
Feed: A source feed. WebSphere sMash can consume feeds of the RSS 0.x, RSS 1.0, RSS 2.0, Atom 0.3, and Atom1.0 types.
-
SortFeed: Sorts feeds. Using SortFeed, feed entries can be sorted in ascending or decending order by attributes such as title or date.
-
AggregateFeeds: Supports two or more input feeds and outputs a single output feed of the Atom 1.0 type.
-
FilterFeed: Enables the flow creator to give the flow selection criteria. You can use keyword or a boolean XPath 1.0 expression to select entries for the output Atom node.
-
Unique: Removes duplicate entries by using an XPath expression to identify the duplicated facet.
-
Truncate: Returns a specific set of entries, such as the first ten entries of the input feed.
Certainly, there are more, but these are sufficient for our purposes. (See
the WebSphere sMash documentation for other available operators.)
Let's build a gadget that uses a WebSphere sMash flow to aggregate a pair of
feeds from IBM developerWorks to WebSphere sMash:
-
If you don’t have Project Zero or WebSphere sMash installed already, you
can install WebSphere sMash Developer’s Edition for free from the Project Zero Web site, which also provides install instructions and documentation.
-
Once installed, you can use the WebSphere sMash App Builder (Figure 4) to start
building your flow. Start by entering the appropriate launch commands from a command prompt.
(The commands in Listing 5 were used to launch the App Buiilder on Linux. To find the commands for your platform, refer to the startAppBuilder script in the
directory where you installed the WebSphere sMash CLI module.)
Listing 5. Launch WebSphere sMash App Builder
vpalat@vpalat-laptop2:~/zero$ ./startAppBuilder
Application started and servicing requests at http://localhost:8070/
CWPZT0600I: Command start was successful
vpalat@vpalat-laptop2:~/zero$ |
Figure 4. WebSphere sMash App Builder
-
To build your feed, you will create a new application by clicking on the
New Application link in the left navigation box (Figure 4).
-
In the New Application panel (Figure 5), describe your application by
entering the following data:
- Application Name:
GadgetFeed
- Application Path:
<HomePath>/GadgetFeed/
- Application Description:
Feed Aggregation for Google Gadgets
Click the Create button to return to the updated main menu
(Figure 6).
Figure 5. New Application panel
-
To edit your application, clicking on the pencil icon (Figure 6). This
will display the GadgetFeed Editor (Figure 7), from which you can add and edit the files for your application.
Figure 6. Main menu with Edit icon
Figure 7. GadgetFeed Editor
-
To create a new flow, you need to create a new file in the public
folder of your application directory by clicking on the New File => New
Page (in /public) (Figure 8).
Figure 8. New Page menu
-
When prompted for the file you want to create, enter /public/gadget/websphere.flow, to be created in the public directory (Figure 9).
Figure 9. File Create
-
App Builder then prompts you to add the zero.assemble.flow dependency
(Figure 10). Click Add.
Figure 10. Add dependency
Why add the dependency? Unlike an application server, which provides all the
class libraries up front, WebSphere sMash adds the dependencies as they are needed so that your application will never have more than exactly what it needs. If you look at your dependency list in Figure 11, you'll see that it only shows the Modulegroups you are using: zero.core, zero.assemble.flow, and zero.flow.management.
Figure 11. Application Dependencies
-
In the activities drop down menu, make sure the selection is set to Feed Activities.
Figure 12. Updating the activities
-
Import feeds for the following URLs by dragging the Feed icon onto the
Editor panel and update the URL values with these (Figure 13):
http://www.ibm.com/developerworks/views/webservices/rss/libraryview.jsp
http://www.ibm.com/developerworks/views/websphere/rss/libraryview.jsp
Figure 13. Adding the feed operators
-
Merge these feeds together using the FeedAgregator. Drag an
aggregateFeeds operator to the editor (Figure 14). Change the title and description to suit, then pull the output of the feeds to the input of the aggregateFeeds widget.
Figure 14. Adding the aggregateFeeds operator
-
To aggregate the feeds, drag the output of the feeds to the input of
aggregateFeeds (Figure 15).
Figure 15. Aggregating the feeds
-
The aggregateFeeds widget will return an Atom object, but in order to get
it back to the requester, you need to include a replyGet entry. A replyGET activity provides the response to an HTTP GET request previously matched to a receive activity. Each flow can only execute to one HTTP response. Drag a replyGet item from the Feed Activities to the Editor. Link it to the aggregateFeeds by taking the output of the aggregateFeeds and dragging it to the replyGet item (Figure 16).
Figure 16. Adding the replyGet
-
The editor will automatically save changes for you. Names of unsaved
files are annotated with an asterisk (*) (Figure 17), as opposed to names of
saved files, which are not (Figure 18).
Figure 17. Unsaved flow
Figure 18. Saved flow
-
To start your flow, click Run in the upper left corner of the
panel (Figure 19). This will start the application running on port 8080.
Figure 19. Flow is stopped
Figure 20. Flow is running
-
Invoke your flow by navigating to http://localhost:8080/gadget/websphere.flow in a browser (Figure 21).
Figure 21. Invoking your feed
-
This looks good, but why does "Integrating IT monitoring and business
activity monitoring" show up in the list twice? It turns out that there are a
few articles that are shared between the two feeds that you you brought together
in this flow. You can fix this by adding the unique operator. Drag the unique operator from the Feed Activities menu to the editor (Figure 22).
Figure 22. Adding a unique operator
-
Remove the link between aggregateFeeds and replyGET by clicking on the
link and then selecting the scissors icon to delete the link (Figure 23).
Figure 23. Removing the link
-
Once you've deleted the link, you can connect the aggregateFeeds to
unique_0, and unique_0 to replyGET_0. Make the article unique by title. To
do this, select atom:title in the unique_0 drop down (Figure 24), save the flow.
Figure 24. Choosing the evaluation
-
Retry http://localhost:8080/gadget/websphere.flow (Figure 25). You will
see that "Integrating IT monitoring and business activity monitoring" now displays only once.
Figure 25. Modified feed of unique entries
 |
Turning a feed into a gadget
Now that you have a feed, let's turn it into a gadget:
-
Start with a basic example with no user preferences, but using some JavaScript to get started (Listing 6).
Listing 6. A simple start
<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="Gadget Sample"
title_url="http://www.ibm.com/developerworks/"
height="200"
author="Jay Palat"
author_email="vpalat@us.ibm.com"/>
<Content type="html">
<![CDATA[
<style> #content_div { font-size: 80%; margin: 5px; background-color: #FFFFBF;}
</style>
<div id=content_div></div>
<script type="text/javascript">
function showfeed__MODULE_ID__() {
var html = "Powered by JavaScript";
_gel("content_div").innerHTML = html;
};
_IG_RegisterOnloadHandler(showfeed__MODULE_ID__());
</script>
]]>
</Content>
</Module> |
In this example, you start with a stylesheet to provide the formatting for
your content. You create a div container (<div
id=content_div></div>) in which to place the content, and you finally have your JavaScript function (Listing 7).
Listing 7.
JavaScript functions
<script type="text/javascript">
function showfeed__MODULE_ID__() {
var html = "Powered by JavaScript";
_gel("content_div").innerHTML = html;
};
_IG_RegisterOnloadHandler(showfeed__MODULE_ID__());
</script> |
The function is showfeed__MODULE_ID__(), with __MODULE_ID__ to be replaced by
the identifier for this particular module. The function itself creates a simple HTML string and places it within the content div you created a few lines earlier. The last line _IG_RegisterOnloadHandler(showfeed__MODULE_ID__()); will run showfeed__MODULE_ID__() on startup. Figure 26 shows what your sample looks like in the Google Gadgets Editor Preview mode.
Figure 26. Preview mode
-
This is a good start to using JavaScript in your gadget, but let's see
how you can incorporate your feed. For this, you will use the _IG_FetchFeedAsJSON command, which takes these attributes:
-
URL: the location of your feed (string)
-
function: function that the gadget engine will use once it has retrieved the feed (JavaScript function)
-
number of entries: number of entries: number of entries to be retrieved from the feed (int, default 3)
-
get summaries: if true, then the function will get the summaries (Boolean, default is false)
So let's incorporate it. Be aware that up until this point, you have only been looking at WebSphere sMash from the localhost. From this point on, your WebSphere sMash instance needs to be publicly available, since the Google infrastructure needs to be able to see the public feed.
Listing 8. New script for displaying feed content
<script type="text/javascript">
var entries = 5;
var summaries = false;
_IG_FetchFeedAsJSON("http://6myhost.com:8080/gadget/websphere.flow", function (feed)
{
if (feed == null){
alert("There is no data.");
return;
}
var html = ""
html += "<div><b>" + feed.Title + "</b></div>";
html += "<div>" + feed.Description + "</div><br>";
if (feed.Entry) {
for (var i = 0; i < feed.Entry.length; i++) {
html += "<div>"
+ "<li><a target='_blank' href='" + feed.Entry[i].Link + "'>"
+ feed.Entry[i].Title
+ "</a> ";
}
html += "</div>";
}
_gel("content_div").innerHTML = html;
},
entries, summaries);
</script> |
In Listing 8, the _IG_FetchFeedAsJSON function was added, and your sMash
instance was added for the feed. This should be as easy as using your Internet accessible IP address for localhost. The function (feed) function is outlined as part of IG__FetchFeedAsJSON. The inline function checks to make sure that you have a feed (if the feed is empty, it returns an alert) before populating the HTML with the details of the feed. The number of entries is set to 5 and set the summaries are set to false. Applying this to the example, a preview is shown in Figure 27.
Figure 27. Preview incorporating feed
-
This is good, but you're only seeing three of your five feed items. To fix this, update the ModulePrefs to permit scrolling. You can do this by adding the scrolling parameter to your gadget code, shown in bold type below.
Listing 9.
Revised gadget
<ModulePrefs title="Gadget Sample"
title_url="http://www.ibm.com/developerworks/"
height="200"
author="Jay Palat"
scrolling="true"
author_email="vpalat@us.ibm.com"/> |
This modification gives you the result in Figure 28.
Figure 28. Scrolling gadget
-
This is good, but you can do better. What if users want more than five entries or want to see the summaries for these articles? You can handle these choices by using user preferences. Add a user preference for number of entries and another for the use of summaries (Listing 10).
Listing 10. Add user preferences
<UserPref name="num_entries" display_name="Number of Entries?" default_value="5"/>
<UserPref name="show_summaries" display_name="Show summaries?" datatype="bool"
default_value="false"/> |
With these <UserPref> entries in the XML, next get the JavaScript to extract them. Use the Google Gadget JavaScript Core Library to get the preferences.
Listing 11.
Extract user preferences
var prefs = new _IG_Prefs(__MODULE_ID__);
var summaries = prefs.getBool("show_summaries");
var entries = prefs.getInt("num_entries"); |
This replaces your previous declarations of summaries and entries. You can
now incorporate this into your gadget logic. The completed gadget with the new logic for handling the summaries appears in bold in Listing 12.
Listing 12. Complete gadget code
<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="Gadget Sample"
title_url="http://www.ibm.com/developerworks/"
height="200"
author="Jay Palat"
scrolling="true"
author_email="vpalat@us.ibm.com"/>
<UserPref name="num_entries" display_name="Number of Entries?" default_value="3"/>
<UserPref name="show_summaries" display_name="Show summaries?" datatype="bool"
default_value="false"/>
<Content type="html">
<![CDATA[
<style> #content_div { font-size: 80%; margin: 5px; background-color: #FFFFBF;}
</style>
<div id=content_div></div>
<script type="text/javascript">
var entries = 5;
var summaries = false;
var prefs = new _IG_Prefs(__MODULE_ID__);
var summaries = prefs.getBool("show_summaries");
var entries = prefs.getInt("num_entries");
_IG_FetchFeedAsJSON("http://66.93.60.86:8080/gadget/websphere.flow", function (feed)
{
if (feed == null){
alert("There is no data.");
return;
}
var html = ""
html += "<div><b>" + feed.Title + "</b></div>";
html += "<div>" + feed.Description + "</div><br>";
if (feed.Entry) {
for (var i = 0; i < feed.Entry.length; i++) {
html += "<div>"
+ "<li><a target='_blank' href='" + feed.Entry[i].Link + "'>"
+ feed.Entry[i].Title
+ "</a> ";
if (summaries==true) {
html += "<br><i>" + feed.Entry[i].Summary + "</i>";
}
}
html += "</div>";
}
_gel("content_div").innerHTML = html;
},
entries, summaries);
</script>
]]>
</Content>
</Module> |
-
To be useful, a gadget needs to be published. Luckily, the Google
Gadgets Editor makes publication easy. From the editor menu, simply select
File => Publish.
Figure 29. Publish gadget
The editor performs a validation check on (Figure 30). Click OK and
your gadget will be published (Figure 31).
Figure 30. Gadget validation
Figure 31. Gadget published
-
While you can publish to the iGoogle directory for everyone to see your
gadget, it might make more sense to publish your gadget to your iGoogle page
while you continue to work on it. Select Add to my iGoogle page
(Figure 31) and you will receive a confirmation message similar to that shown
in Figure 32. Your gadget is now on iGoogle.
Figure 32. Add to iGoogle confirmation
With these steps completed, your gadget is now available on your iGoogle
page, as shown in Figure 33.
Figure 33. iGoogle Web page with sample gadget
Conclusion
This article examined how to create a gadget, how to use the different parts
of the gadget XML specification, and how you can create a feed assembly in
WebSphere sMash using the AppBuilder GUI. The gadget and the feed assembly
were then combined, using a feed generated by WebSphere sMash to power the
gadget. By applying these instructions to your own situational application
requirements, you can build a gadget from scratch and deploy it
to your iGoogle home page.
Acknowledgements
The author thanks Catherine Rivi, Karl Bishop, and Mary Ann Johnson for their help in reviewing and commenting on this article.
Resources Learn
Get products and technologies
Discuss
About the author  | 
|  |
Vijaykumar Palat is a Consulting IT Specialist for IBM as a member of IBM Software Services WebSphere for IBM, servicing the IBM Global Account. Jay has been a lead architect and developer for IBM internal projects, as well as for external clients and business partners, developing solutions for WebSphere Application Server and WebSphere Portal. He has an active interest in Web 2.0 technolgies, open source, and virtualization. He has a Masters of Information Technology from Carnegie Mellon University and BS in CS from University of Pittsburgh. |
Rate this page
|  |