Build server-side mashups with Geronimo and REST

Use REST, Ajax, and Apache Geronimo to build a mashup for Twitter and Google Maps

Discover the techniques and technologies you can use to build a mashup application using Apache Geronimo, a REST-based protocol, and data from multiple sources. The mashup combines data from Google Maps and Twitter tweets to pinpoint Twitter users as they update their Twitter status.

Share:

J. Jeffrey Hanson, Chief Architect, Max International

Jeff HansonJeff Hanson has more than 20 years of experience in the software industry, including working as senior engineer for the Microsoft Windows port of the OpenDoc project, lead architect for the Route 66 framework at Novell, and chief architect for eReinsure.com, Inc., where he directed design and implementation of frameworks and platforms for J2EE-based reinsurance systems. Jeff is currently the CTO for Max Software, Inc., where he directs efforts to provide desktop and enterprise applications and platforms for Internet safety and parental controls. Jeff is the author of numerous articles and books, including .NET versus J2EE Web Services: A Comparison of Approaches, Pro JMX: Java Management Extensions, and Web Services Business Strategies and Architectures.



21 October 2008

Also available in Japanese Vietnamese

The term mashup originally defined a technique in music for mixing two or more songs in a manner that created an entirely new piece. In software engineering, mashup implies technologies and patterns for combining data, UI components, and processes to create new Web applications and sites.

Mashups are popular among site developers because of the ease with which data and content can be combined. This ease comes from the availability and ubiquity of dynamic and semantically rich technologies for the Web — technologies such as XML, JavaScript Serialized Object Notation (JSON), Resource Description Framework (RDF), dynamic JavaScript, and Ajax. These and other technologies provide an unlimited degree of possibilities for creative content developers.

Frequently used acronyms

  • Ajax: Asynchronous JavaScript + XML
  • API: Application program interface
  • CSS: Cascading Style Sheets
  • DOM: Document Object Model
  • HTML: Hypertext Markup Language
  • HTTP: Hypertext Transfer Protocol
  • UI: User interface
  • XML: Extensible Markup Language
  • XSD: XML Schema Document

You typically create mashups by combining UI components, services or processes, and data. Mashable UI components include dynamic JavaScript, HTML snippets, RSS feeds, and results from Web service API invocations. Mashups depend on mixing loosely coupled UI components, processes, or data from one or more sites using data transformations, dynamic JavaScript, DOM manipulation, and other techniques. The current stereotypical mashup involves combining a map from Google Maps with location data, such as crime statistics and real estate prices for a given region.

This article discusses how to use APIs provided by Twitter and Google Maps together with Ajax and Java™ language code to build a mashup you can deploy and execute in an Apache Geronimo environment.

(Editor's note: This article discusses one of many ways to create server-side mashups. WebSphere® sMash offers another means to build Web-based applications using dynamic scripting. See Resources for more information about the software discussed in this article and where to find it.)

Getting to know Geronimo

Geronimo is a fully compliant Java Platform, Enterprise Edition (Java EE) platform you can use to build enterprise services and applications. Geronimo is designed around an architecture that uses Inversion of Control (IoC) techniques to decouple services and components to a high degree. This high degree of decoupling makes a very modular and configurable deployment and execution environment. Geronimo promotes the use of Java Management Extensions (JMX) and a similar Geronimo-specific MBean-style framework that makes Geronimo a powerful and manageable enterprise platform.

You create a Geronimo runtime environment using a customizable aggregation of modules assembled and managed by a lightweight kernel engine. The kernel engine is a Geronimo module, which is an arbitrary set of classes, other modules, and a serializable configuration. A Geronimo kernel loads, assembles, and orchestrates modules when a Geronimo runtime instance is started. Assembled modules determine the functionality for a Geronimo deployment and execution environment. All services in a Geronimo runtime instance are deployed as modules.

Geronimo modules are defined within an XML document known as a deployment plan. A final Geronimo deployment plan consists of a combination of an original deployment plan, a Maven project.properties file, and a Maven Project Object Model (POM) file. Figure 1 shows how these artifacts are processed to create a final deployment plan.

Figure 1. Processing Geronimo deployment plans
Processing Geronimo deployment plans

The contents of a deployment plan are controlled by an XSD file. A deployment plan defines a module ID, environment properties for a module, a module's dependencies, services the module provides, and Geronimo Beans (GBeans) for the module, among other things. Listing 1 illustrates a simple Geronimo deployment plan.

Listing 1. Example Geronimo deployment plan
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="http://geronimo.apache.org/xml/ns/deployment-1.1">
  <environment>
    <moduleId>
      <groupId>geronimo</groupId>
      <artifactId>example</artifactId>
      <version>1.0.0</version>
      <type>car</type>
    </moduleId>  
    <dependencies>
      <dependency>
        <groupId>geronimo</groupId>
        <artifactId>j2ee-server</artifactId>
        <type>car</type>
      </dependency>
    </dependencies>
    <hidden-classes/>
    <non-overridable-classes/>
  </environment>
  
  <gbean name="ExampleService" class="com.example.myservices.MyServiceGBean">
    <attribute name="prop1">12345</attribute>
    <attribute name="prop2">This is a value for a property</attribute>
  </gbean>
</module>

When the build process compiles a deployment plan like the one in Listing 1, a Configuration Archive (CAR) file is created with a unique name. The unique name generated for the configuration in Listing 1 would be geronimo/example-1.0.0/car.

Geronimo CAR files

A Geronimo CAR file is a Java Archive (JAR) file containing the serialized form of a deployment plan and any other additional resources. A serialized deployment plan is contained in a file named config.ser in the META-INF directory of the CAR. CAR files are created by a Geronimo packaging plug-in for Maven during the build process.

Geronimo repositories

A Geronimo repository is a registry of artifacts typically embodied as a folder hierarchy on a file system. The binary distribution of Geronimo provides a folder named repository that contains all dependencies for modules that represent the core Geronimo platform.

In Geronimo, an artifact is an arbitrary component, such as a Web Archive (WAR) file, a JAR file, or a CAR file, stored in a Geronimo repository. An artifact is stored in command-line deployment utilities or the Geronimo Web console provided with a Geronimo distribution.

Downloading and installing Geronimo

Download the Geronimo platform (see Resources), then extract the files to a directory I'll refer to as GERONIMO_HOME. With Geronimo installed, open a command-line console and change to the GERONIMO_HOME/bin directory, then run startup.sh. This starts the server in a new console window. When the server is started, you should see a screen similar to Figure 2.

Figure 2. Geronimo startup console
Geronimo startup console

The startup console shows modules, connectors, and applications that are loaded and started as the Geronimo runtime environment is established. You can stop the Geronimo runtime using the shutdown.sh script, also found in the GERONIMO_HOME\bin\ directory of your Geronimo installation.


Getting to know REST

Representational State Transfer (REST) is a software architecture pattern and invocation style for accessing and updating resources on a network. The term REST, defined in a dissertation by Roy Thomas Fielding (see Resources), is often used to describe transmitting data across HTTP using standard requests and responses defined by the default set of HTTP methods: GET, POST, DELETE, and PUT.

The REST architectural style strongly suggests the use of uniform resource identifiers (URIs) to locate and access representations of resources — known as the representational state of the resource. Representational state can be accessed and modified using a limited set of create, read, update, and delete (CRUD) verbs with little additional overhead on top of what standard protocols like HTTP provide. REST-based conversations should remain stateless, thereby making it a prime facilitator for semantic data formats such as RSS, RDF, the Web Ontology Language (OWL), and Atom.


Getting to know Google Maps

The Google Mpas APIs provide several ways to manipulate maps or create and retrieve maps that can be embedded within a Web page. Of interest to this article is the ability the Google Maps APIs provide to retrieve a map based on a given location string.

The public interface of Google Maps

Google Maps provides APIs you can access using standard HTTP requests. Each API request requires an API key you need in advance. The Google Maps APIs support HTTP responses in the KML and GeoRSS data formats.

Using the Google Maps APIs

To begin using the Google Maps APIs, you must first sign up to receive an API key from the Google Code site. The key you receive will be valid for a single Web domain.

Retrieving a map with Google Maps

Google Maps exposes an API that provides the ability to retrieve a geo-coded rendition of a given location. To retrieve the location and other information about a user, you simply make a standard HTTP GET request using http://maps.google.com/maps/geo?q=Salt+Lake+City%2C+UT&output=xml&key={apikey}. You must replace {apikey} with the API key Google assigns you. The example below shows how to use this API with curl from a command line.

C:\>curl -G http://maps.google.com/maps/geo?q=Salt%20Lake%20City
   %2C%20UT%26output=xml%26key=ABQIAAAA7kHuyDenRy7D_
   kXwDkUfhBQCabR54RQscLxLTjQlrb8wKm07EBRSANMlyMuVIxp6jUQazrN52Pzp3w

The response will be returned as XML-based data known as KML containing information about the given location. KML is an open standard maintained by the Open Geospatial Consortium Inc. (OGC). An example of this KML data for the location, Salt Lake City, is shown below.

Listing 2. Example of a KML document
<kml xmlns="http://earth.google.com/kml/2.0">
  <Response>
    <name>Salt Lake City, UT</name>
    <Status>
      <code>200</code>
      <request>geocode</request>
    </Status>
    <Placemark id="p1">
      ...
      <Point>
        <coordinates>-111.888189,40.771592,0</coordinates>
      </Point>
    </Placemark>
  </Response>
</kml>

In this listing, the <coordinates> element contains the values specifying the longitude, latitude, and altitude for Salt Lake City, respectively.


Getting to know Twitter

Twitter provides a service that allows users to connect to each other using standard HTTP requests, instant messages, and text messages. The premise of the Twitter service is for users to send short messages, known as tweets, describing what they're doing. The tweets are delivered to the Twitter servers and relayed to "followers" of the user sending the tweets.

The public interface of Twitter

Twitter provides a set of APIs you can access using standard HTTP requests. The Twitter APIs are based loosely around the REST architecture. Each Twitter API currently supports HTTP responses in the XML, JSON, RSS, and Atom data formats. An example of using a Twitter API with curl from a command line to retrieve the Twitter public timeline in RSS format: C:\>curl -G http://twitter.com/statuses/public_timeline.rss.

Other APIs are available for performing such tasks as retrieving a friend's timeline, posting status updates, updating your profile location, and retrieving the profile data for a user.

Retrieving a Twitter user's location

Twitter exposes an API that provides the ability to retrieve the location property of a user profile. To retrieve the location and other information of a user, you simply make a standard HTTP GET request using http://twitter.com/users/show/{targetUserID}.xml. You must replace {targetUserID} with the Twitter ID of the user for whom the information is to be retrieved. An example of using this API with curl from a command line is shown here:

C:\>curl -G http://twitter.com/users/show/jhanson583.xml

The response is returned as XML-formatted data containing information about the user, including the user's location as configured in the user's profile. An example of this XML data for user jhanson583 is shown below.

Listing 3. Example of XML data for a Twitter user profile
<?xml version="1.0" encoding="UTF-8"?>
<user>
  <id>10852552</id>
  <name>Jeff Hanson</name>
  <screen_name>jhanson583</screen_name>
  <location>Salt Lake City, UT</location>
   …
</user>

Here, the <location> element contains the data entered into the profile of user jhanson583. Note that you can type the string free-form, so it's not guaranteed to be valid for applying to a Google Maps query. Therefore, map data can only be retrieved for user profiles that contain valid location data in the <location> element. The location data shown in the profile above is valid for application to a Google Maps query.

Retrieving a Twitter user's location with Java code

Retrieving a user's profile information in the Java language is simply a matter of sending an HTTP GET request and parsing the response. The snippet in Listing 4 provides Java code for sending a generic HTTP GET request.

Listing 4. HTTP request example in the Java language
  public static String sendHTTPRequest(String url)
    throws Exception
  {
    String res = null;

    try
    {
      HttpURLConnection con = null;
      InputStream inStream = null;
      OutputStream outputStream = null;

      try
      {
        con = (HttpURLConnection) new URL(url).openConnection();
        con.setDoInput(true);
        con.setRequestMethod("GET");
        inStream = con.getInputStream();
        res = parseHTTPResponse(inStream);
      }
      finally
      {
        try
        {
          inStream.close();
          outputStream.close();
          con.disconnect();
        }
        catch (Exception e)
        {
        }
      }
    }
    catch (IOException e)
    {
      e.printStackTrace();
    }

    return res;
  }

  public static String parseHTTPResponse(InputStream inStream)
    throws IOException
  {
    BufferedReader br = null;
    br = new BufferedReader(new InputStreamReader(inStream, "UTF-8"));
    StringBuffer buf = new StringBuffer();
    String line;
    while (null != (line = br.readLine()))
    {
      buf.append(line).append("\n");
    }
    return buf.toString();
  }

In this listing, an HttpURLConnection element is used to send the GET request. The response from the request is retrieved as an InputStream that is read line by line to access the XML data. Using the code shown above for sending an HTTP request, you can make a call to the Twitter API for retrieving a user profile, as shown below.

Listing 5. HTTP request for retrieving Twitter profile data in the Java language
public class Twitter
{
  private static final String API_URL = "http://twitter.com/users/show/";

  public static String getUserLocation(String targetUserID)
    throws Exception
  {
    String response =
      HTTP.sendHTTPRequest(API_URL + targetUserID + ".xml", null);
    DocumentBuilder docBuilder =
       DocumentBuilderFactory.newInstance().newDocumentBuilder();
    Document doc =
      docBuilder.parse(new InputSource(new StringReader(response)));
    if (null != doc)
    {
      NodeList nodeList = doc.getElementsByTagName("location");
      if (null != nodeList && nodeList.getLength() > 0)
      {
        Node locationNode = nodeList.item(0);
        if (null != locationNode)
        {
          return locationNode.getTextContent();
        }
      }
    }

    throw new Exception("Invalid HTTP response content encountered");
  }
}

Getting to know Ajax

Ajax is a collection of techniques and concepts for interacting with an HTTP server from a JavaScript-enabled browser without directly refreshing the page to invoke the server requests. Ajax is also a collection of techniques and concepts for dynamically updating browser pages using JavaScript, CSS, DOM manipulation, etc. The ability to retrieve data from a server through a browser page and to apply the data without refreshing the page has facilitated an atmosphere for creating browser-based applications with a rich UI experience that rivals desktop applications.

Ajax-based pages rely on the use of a JavaScript object known as the XMLHttpRequest object. This object is used to transmit HTTP requests from a browser to a server and receive responses from the server synchronously or asynchronously.

The snippet in Listing 6 creates an instance of the XMLHttpRequest object in the ajaxPost method. The XMLHttpRequest object is then used to invoke a standard HTTP POST method on the server framework. The data retrieved from the server framework is directed through the XMLHttpRequest object's send method, where it is retrieved, parsed, and passed to the createMap method from which the Google Maps map is created and applied to the page.

Listing 6. Processing an XMLHttpRequest request and response
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0
   Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml">
<head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
  <title>Google Maps</title>
  
  <script src="http://maps.google.com/maps?file=api&v=2&key=..."
    type="text/javascript"></script>

  <script type="text/javascript">
    //<![CDATA[

    if (GBrowserIsCompatible())
    {
      // =============================================
      // createMarker function
      // =============================================
      function createMarker(point, html)
      {
        var marker = new GMarker(point);
        GEvent.addListener(marker, "click", function()
        {
          marker.openInfoWindowHtml(html);
        });
        return marker;
      }

      // =============================================
      // createMap function
      // =============================================
      function createMap(latitude, longitude, twitterUser)
      {
        // Display the map, with some controls, and set the initial location
        var map = new GMap2(document.getElementById("map"));
        map.addControl(new GLargeMapControl());
        map.addControl(new GMapTypeControl());
        map.setCenter(new GLatLng(latitude, longitude), 8);

        // Set up marker

        var point = new GLatLng(latitude, longitude);
        var marker = createMarker(point, twitterUser)
        map.addOverlay(marker);
      }
      
      // =============================================
      // ajaxPost function
      // =============================================
      function ajaxPost(apiURL)
      {
        var twitterUser = document.getMapForm.TwitterUser.value;
        var xmlRequest = new XMLHttpRequest();

        // The false parameter indicates a synchronous call
        //
        xmlRequest.open("POST", apiURL + '?TwitterUser=' + twitterUser, false);
        xmlRequest.send(twitterUser);
        if (xmlRequest.status == 200)
        {
          if (xmlRequest.responseText)
          {
            var xmlDoc = xmlRequest.responseXML;

            var latitude =
              xmlDoc.getElementsByTagName('latitude')[0].firstChild.data;
            var longitude =
              xmlDoc.getElementsByTagName('longitude')[0].firstChild.data;

            createMap(latitude, longitude, twitterUser);
          }
        }
        else
        {
          alert("ajaxPost failed with status: " + xmlRequest.status);
        }
      }
    }
    else
    {
      // display a warning if the browser was not compatible
      alert("Sorry, the Google Maps API is not compatible with this browser");
    }

    //]]>
  </script>
</head>

<body onunload="GUnload()">

<p/>

<form method="POST"
    action="javascript:ajaxPost('http://localhost:8080/twoogle/service/getMap')"
    name="getMapForm">
  Twitter user name: <input type="text" value="" name="TwitterUser"/>
  <input type="submit" value="Get Map" name="submit"/>
</form>

<p/>

<div id="map" style="width: 550px; height: 450px"></div>
</body>

</html>

The code uses the latitude and longitude retrieved for a given Twitter user to create a Google Maps map. The map is then applied to the DOM of the page, where it becomes a child element of the map <div> element.


Combining Twitter and Google Maps data using REST and Ajax techniques

With the ability to retrieve location data for Twitter users and Google Maps geo-code data, you can build related services to publish Twitter data and Google Maps data that mashups can consume and combine to produce an aggregated UI from the combined data.

The framework showcased in this article combines data from service invocations that retrieve location data about a Twitter user's profile, as well as longitude and latitude data from Google Maps for the location data. The data is then retrieved using Ajax techniques from JavaScript in a mashup page, where the data is dynamically applied to the DOM of the page to show a map with the Twitter user's location. The source project for this example is available in the Download section.

The flow of control for the framework is quite simple: Instigate a synchronous REST-based service call using Ajax from the mashup to a server-side Ajax-savvy Java servlet. The service invocation is structured as http://{host}:{port}/twoogle/service/getMap. The servlet dispatches service invocations to Twitter and Google Maps to retrieve a Twitter user's location. The latitude and longitude for the user's location is passed back to the mashup and applied to the page to create a map. The framework is shown in Figure 3.

Figure 3. The Twitter/Google Maps mashup framework
The Twitter/Google Maps mashup framework

The framework receives Ajax-based HTTP requests from a mashup client, then uses the data from the requests to retrieve location data from Twitter and Google Maps. Latitude and longitude values for a given Twitter user are passed back to the mashup client as XML to be parsed and applied to the mashup page.


Deploying and running the framework

The Twitter/Google Maps Ajax and REST framework exposes one servlet that receives Ajax-based, REST-structured HTTP requests. The data from the requests is passed to Twitter and Google Maps services to retrieve location data for a given Twitter user.

To deploy the framework, you package the compiled classes for the framework along with static HTML files and other resources in a folder structured as an exploded WAR file. The command shown below is then applied to the exploded WAR file folder to deploy the framework to Geronimo.

GERONIMO_HOME\bin>deploy --user system 
  --password manager deploy --inPlace MYAPPDIR\MYAPPNAME

Be sure to replace MYAPPDIR and MYAPPNAME with the location of your application and the name of your application, respectively. You can use the command shown below to undeploy the framework from Geronimo.

GERONIMO_HOME\bin>deploy --user system 
  --password manager undeploy MYAPPNAME

Again, be sure to replace MYAPPNAME with the name of your application.


Test the framework

After the application has been deployed successfully, type the following URL into a Web browser: http://host:port/twoogle. When the page appears, type a Twitter user name in the box at the top of the page, then click Get Map. If the user's profile contains a valid <location> element, a Google Maps map appears on the page.

Conclusion

Apache Geronimo is a Java EE platform you can use to build enterprise services and applications. Geronimo is designed around decoupled services and components to facilitate a very modular, manageable, and configurable deployment and execution environment. Mashups have gained popularity among site developers because of the possibilities presented and the ease with which data and content can be combined. This ease comes from the availability and ubiquity of dynamic and semantically rich technologies for the Web — technologies such as XML, JSON, RDF, dynamic JavaScript, and Ajax.

This article discussed techniques for combining REST-style service invocations with Ajax-based clients and servers in a Java framework to retrieve location data for a given Twitter user. The location data was then used to retrieve latitude and longitude data from Google Maps, which were in turn applied using client-side Ajax techniques to create a Google Maps map dynamically applied to a mashup page.

Your task now is to download the latest version of Geronimo, familiarize yourself with Twitter's APIs and the Google Maps APIs, and research the general concepts of mashups. Using the simplified model of Web application programming that REST and Ajax afford, you can now begin to use the power of Geronimo, Twitter, and Google Maps to build robust and highly flexible mashups.


Download

DescriptionNameSize
Sample projectos-ag-mashup-rest-TwitterFramework.zip13KB

Resources

Learn

Get products and technologies

  • Download the latest version of Apache Geronimo.
  • WebSphere sMash includes the application builder and a runtime to support development, testing, and limited deployment of applications.
  • Innovate your next open source development project with IBM trial software, available for download or on DVD.
  • Download IBM product evaluation versions, and get your hands on application development tools and middleware products from DB2®, Lotus®, Rational®, Tivoli®, and WebSphere.

Discuss

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 Open source on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source
ArticleID=346358
ArticleTitle=Build server-side mashups with Geronimo and REST
publish-date=10212008