Mashup explanation – part I

My AJAX
presentation
 from the Versailles JavaDay a couple
of weeks ago featured a mashup quickly written using a JSF component to
put
on a map all participants
to the event. The code was pretty
ugly as I only had a few days to write the app from the day I found out
that Google
had a Geo service
with good results in Europe. The
application has two parts:

1/ a batch Java SE 6 (b86) application reading a PARTICIPANTS database
using JPA (Java Persistence API), doing some address cleaning/normalization (haven’t cleaned that part), invoking the geo web service, parsing the JSON result, and
storing the resulting latitude/longitude in a COORDINATES
table. Again, the reason for not making this address resolution an
interactive part of the application is the time taken by this which
is due to (in no particular order) the Google “1 request per
1.75 seconds” limitation, the request itself and the result parsing
time.

2/ a single page Web Application built with Java Studio Creator using
the JSF maps component which simply reads the COORDINATES
table to build a collection of MapMarkers
which is then bound to the component’s markers
property. The COORDINATES table is the only
link between the two parts of the application.

Part IJPA in Java SE, Geo-coding invocation
JSON parsing.
Part II
– Writting geo
data back to the database, Web Service invocation &
Google Map
markers construction (give me another day or so before I publish part
II).

JPA in Java SE
Retrieving data from the PARTICIPANTS table
was quite easy using the JPA (Java Persistence API) and NetBeans “Entity
Class from Database”
feature (a simple Participants
entity class is created from the PARTICIPANTS
table). 

The persistence.xml configuration for the
persistence unit is quite simple:

<?xml version="1.0" encoding="UTF-8"?>
<persistence
version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
  <persistence-unit name="GeoCoordPU"
transaction-type="RESOURCE_LOCAL">
   
<provider>oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider</provider>
   
<class>bo.Participants</class>
   
<class>bo.GeoCoordonnees</class>
   
<class>bo.GeoCoordonneesFull</class>
    <properties>
      <property
name="toplink.jdbc.user" value="nbuser"/>
      <property
name="toplink.jdbc.password" value="NOT_TELLING"/>
      <property
name="toplink.jdbc.url"
value="jdbc:derby://localhost:1527/javaday06"/>
     
<property
name="toplink.jdbc.driver"
value="org.apache.derby.jdbc.ClientDriver"/>

      <!--
      <property
name="toplink.logging.level" value="FINEST"/>
      -->
    </properties>
  </persistence-unit>
</persistence>

It uses Toplink as the provider (default in GlassFish)
and Derby (JavaDB)
as the database. Note this is all done outside the container and only required adding the Toplink Essential library to the project).

Reading the data from the table goes like this:

    private EntityManagerFactory
factory;
    private EntityManager em;
   
private
List<Participants> allParticipants;

    
    public FetchCoordinates() {
       
factory
= Persistence.createEntityManagerFactory("GeoCoordPU");


       
em = factory.createEntityManager();

       
initData();
        ...
    }

    private void initData() {
       
EntityTransaction tx = null;
       
try {
           
tx = em.getTransaction();
           
tx.begin();
            
allParticipants =
em.createQuery("SELECT p FROM PARTICIPANTS p").getResultList();

           
tx.commit();
       
} catch (RuntimeException e) {
           
if (tx != null && tx.isActive()) {
               
tx.rollback();
           
}
           
System.out.println("### Error: " + e);
        }
    }

Since,
there is no transaction demarcation provided in this un-managed Java SE
environment, there’s a bit a boiler-plate for hand-doing it. All there’s left to do is cycle through allParticipants
to submit each address to the Geo web service.

You can read more on this subject here:
“Stand-alone
Persistence in a J2SE Project”

– “An
Introduction to Java Persistence for Client-Side Developers”
.

Geo-coding invocation
The Geo web service provided by Google, unlike it’s SOAP API is
REST-based and is described here.
I
used the Apache HTTP Common API (talk about dependencies, I had to drag
in 3 jars) to HTTP GET the url and decided to request a JSON-formatted
response. When registering
for a Google key
,
you need to give the URL from which it’ll be used. In my case, it’s not
a web app, so no referrer. I had some trouble figuring out why my
Google key would not always work. Registering using http://localhost
and making sure to use an HTTP GET
seemed to do the trick.

    HttpMethod method = null;
       
    // Build request URL with JSON result
    String
urlAddress = "http://maps.google.com/maps/geo?q=" + address +
"&output=json&key=" + gKey;

       
    try {
       
// Using Apache HTTPClient
       
HttpClient client = new HttpClient();
       
method = new GetMethod(urlAddress);
           

       
int reqStatusCode = client.executeMethod(method);
       
if (reqStatusCode != HttpStatus.SC_OK) {
           
System.err.println("\*\*\* Method failed: " + method.getStatusLine());
        }
           

       
InputStreamReader reader = new InputStreamReader( method.getResponseBodyAsStream()
);
       
BufferedReader bReader = new BufferedReader(reader);
        // read the result in JSON
format
       
String str = bReader.readLine();
        ...
    }

The address
was cleaned in various ways and if needed, subsequent
invocations were
tried with only the city name, in the end leaving unresolved mostly
miss-spelled
addresses.

JSON
parsing

I’m still not sure why I chose json
formatting over the XML alternative. My initial code was basic string
manipulation (it did the job, but boy, like any String manipulation
code, was it ugly!). JSON is less verbose than XML, readable by many languages,
and its data is a valid JavaScript object, so I decided this was a good
occasion to use the JavaScript Rhino engine that is now part
of Mustang
(Java 6) to do the parsing.

public class JSONGeoEngine {
    ScriptEngineManager manager;
    ScriptEngine engine;
    Compilable compilable;
    CompiledScript resultScript=null,
latitudeScript=null, longitudeScript=null;
    // use the JavaScript compiler to
evaluate the JSON result
    private
final String jsonString2Object = "var jsonObject = eval('(' +
jsonResult + ')');";

    private Bindings bindings;
    String currentJSONresult = null;
    
    public JSONGeoEngine() {
       
manager = new
ScriptEngineManager();


       
engine = manager.getEngineByName("js");

        
       
// use this for better performance
       
compilable = (Compilable) engine;
       
bindings = engine.createBindings();
        
       
try {
       
    resultScript
= compilable.compile( jsonString2Object +
       
        " result
=
jsonObject.Status.code;");

            //
Using Placemark[0] and ignoring multiple results.
   
       
latitudeScript = compilable.compile( jsonString2Object +
       
        " result
=
jsonObject.Placemark[0].Point.coordinates[1];");


  
        
longitudeScript = compilable.compile( jsonString2Object +
       
        " result
=
jsonObject.Placemark[0].Point.coordinates[0];");

       
} catch (ScriptException ex) {
           
ex.printStackTrace();
        }

    // setJSONresult() needs to be called
first
    public int getResultCode() {
       
int returnCode = -1;
       
try {
           
Double r
= (Double)resultScript.eval(bindings);

           
returnCode = r.intValue();
       
} catch (ScriptException ex) {
           
ex.printStackTrace();
           
returnCode = 500; // dummy, non-200 error
        }
       
return returnCode;
    }
    
    public void setJSONresult( String result
) {
       
if ( (currentJSONresult !=
null) && 
(currentJSONresult.equalsIgnoreCase(result)) ) {
           
System.out.println ("WARNING: Passed the same JSONresult twice: "+
result);
        }
       
currentJSONresult = result;
       
bindings.put("jsonResult",
currentJSONresult);

    }
    
   
// setJSONresult() needs to be called first

    public void
obtainCoordinates (Coordinates coord) {
       
try {
           
coord.setLatitude(
latitudeScript.eval(bindings).toString() );


           
coord.setLongitude( longitudeScript.eval(bindings).toString() );

       
} catch (ScriptException ex) {
           
ex.printStackTrace();
        }
    }

The key code is in the jsonString2Object
object. The constructor does some setting up to use compiled scripts to
gain performance. I still need to run a quick benchmark to measure the
gain of the interpreted version (and maybe also against the ugly Java String manipulation version).

I’m not sure JSON is the best approach here (especially
given I need to retrieve multiple results from a single JSON result
which required multiple calls to eval()),
but it proved to be quite easy to set up.
Alternatives here could be:
– using the Java
objects for JSON

– using the XML format and JAXB
2.0
to bind to Java objects (also now part of Mustang)
– using the XML format and a regular SAX parser (has been in the JDK
for ages)

(part II real soon).

Author: alexismp

Google Developer Relations in Paris.