Mashup explanation – part II

This is part II of my little
mashup demo
.

Part I
JPA in Java SE, Geo-coding invocation & JSON parsing.
Part II
– Writting
geo data back to the database
Web Service invocation
& Google
Map markers construction
.

Writting
geo data back to the database

Again, I used the same “Entity
Class from Database”
feature to generate an Entity Coordinates
class after creating a simple COORDINATES
table:

drop table "COORDINATES";
create table "
COORDINATES"
(
    "ID" INTEGER not null primary key,
    "MESSAGE" VARCHAR(100),
    "LONGITUDE" VARCHAR(10),
    "LATITUDE" VARCHAR(10)
)

Since I wanted the primary key to be auto-generated, I had to add this
annotation to the id field:

@Entity
@Table(name = "COORDINATES")
public class Coordinates implements Serializable {
    @Id
    @Column(name = "ID", nullable = false)
   
@GeneratedValue(strategy=GenerationType.AUTO)

    private Integer id;
    ...
}

Then, the persistence code is straight-forward just like the one for
reading the data.

    coord =
new Coordinates();

    // set coord's longitude and latitude for the
given address
    geoWorker.resolveAdress ( adresse, coord );
    // set message using HTML
    coord.setMessage
( "<B>" + lastName.toUpperCase() + "</B> "
+ firstName + " " + zipCode + " " + city );

    EntityTransaction tx = null;
    try {
        tx = em.getTransaction();
        tx.begin();
        em.persist(coord);
        tx.commit();
    } catch (RuntimeException e) {
        if (tx != null
&& tx.isActive()) {
           
tx.rollback();
        }
        System.out.println("###
Error: " + e);
    }

Once the COORDINATES table has
been populated, it can be used by the JSF Web Application.

Web Service
invocation

To spice things up a bit I decided to use the traditional
Google SOAP search API to retrieve the first hits for a “LastName + FirstName + Blog”
search and check if any was a blog. In case I do find a blog (simply looking for occurences of "application/atom+xml"
or "application/rss+xml"
tags), I
add HTML code with this page URL to Coordinates.setMessage().
I tried using the wsimport tool from JAX-WS 2.0,
but it failed on the (years-old) Google-provided WSDL. Need to investigate…
Anyway, here’s the traditional
code (requires adding googleapi.jar to the
project libraries) :

    String blogURL = searchForBlog
( lastName + " " + firstName + " blog");

    if (blogURL != null ) {
        message
+= "<br><a href=\\""+ blogURL +
"\\">Blog</a>";
    }

    ...

    private String searchForBlog(String
query) {
       
String result = null;
       
try {
           
GoogleSearch s = new GoogleSearch();
           
s.setKey("MygOOgLEkeY");
           
s.setQueryString(query);

           
GoogleSearchResult r = s.doSearch();


           
GoogleSearchResultElement[] hits = r.getResultElements();

           
for
( int j = 0; j<5; j++ ) {    // If
your blog is not
in the top 5, it's probably dead
               
String
currentURL = hits[j].getURL();

               
if ( isPageABlog(
currentURL )
) {
                   
return currentURL;
               
}
           
}
       
} catch (GoogleSearchFault gsf) {
           
System.out.println(gsf.toString());
        }
       
return result;
    }

and here’s how I check if a page is a blog :

    private boolean
isPageABlog(String url) {
       
boolean found = false;
       
HttpMethod method = null;
       
       
// Using Apache HTTPClient
       
HttpClient client = new HttpClient();
       
method = new GetMethod(url);
       
       
int reqStatusCode = -1;
       
try {
           
reqStatusCode = client.executeMethod(method);
           
if (reqStatusCode != HttpStatus.SC_OK) {
               
System.err.println("\*\*\* Method failed: " + method.getStatusLine());
           
}
           

           
InputStreamReader reader;
           
reader = new InputStreamReader(method.getResponseBodyAsStream());
           
BufferedReader bReader = new BufferedReader(reader);
           

           
String content;
           
found = false;
           
while ( ((content = bReader.readLine()) != null) &&
(!found) ) {
               
if
( (content.indexOf("application/rss+xml") != -1) ||


                    
(content.indexOf("application/atom+xml") != -1 ) ) {

                   
found = true;
               
}
           
}
       
} catch (IOException ex) {
           
ex.printStackTrace();
        }
       
return found;
    }

You could try to add an image to the message after similarly querying http://images.google.com/.

Google Map
markers construction (Using Java Studio Creator

The first simple step is to drop a “Map
Viewer” component
(from the “Blueprints AJAX” category after
having installed them
) on the grid and to set it’s Google key in the
properties window (this one needs to be generated
using the appropriate referrer URL).

Since the COORDINATES table holds all the
data, the first thing to do is to create a new Data Source pointing to
what we’ve just created (in the “Servers” tab, simply right-click on “Data Sources”, and choose “Add Data Source…”) :


Make sure the URL matches the one you used when populating the table.

To have access to this data from the application, simply drag
the COORDINATES
table (which should now be available as part of the newly created Data Source reference) and drop it in the page background. Java Studio
Creator creates a
JDBC RowSet and a DataProvider (an abstraction so you don’t have to
worry with low-level details such as the fact that you’re talking to a
relational database) :

In the managed bean for my page (“Java View” of the page) I create a markers property with a getter.

    private MapMarker[] markers;
    public MapMarker[] getMarkers(){
       
return markers;
    }

In the same class, the init() method is used
to read the data from the database. No JPA here for now (rather a good
old RowSet). The NetBeans
Creator Pack
should make this easier (first bits available real soon now).

    public void init() {
        ...
       
List listOfMarkers = new ArrayList();

       
// read coordinates from file or database
        populateListOfMarkers(listOfMarkers);

       
markers = new MapMarker[listOfMarkers.size()];
       
int index = 0;
       
for (Iterator i = listOfMarkers.iterator(); i.hasNext();) {
           
MapMarker
currentMarker = (MapMarker)i.next();

           
markers[index++] = currentMarker;
        }
    }

    private void
populateListOfMarkers(List listOfMarkers) {
       
try {
           
getCoordinatesDataProvider().getCachedRowSet().execute();
           
while ( getCoordinatesDataProvider().cursorNext() ) {
               
String
message = (String)getCoordinatesDataProvider().
       
       
    getValue("COORDINATES.MESSAGE");

               
Double longitude = new
Double((String)getCoordinatesDataProvider().
       
       
    getValue("COORDINATES.LONGITUDE"));
               
Double latitude = new
Double((String)getCoordinateDataProvider().
       
       
    getValue("COORDINATES.LATITUDE"));
               
MapMarker
marker = new MapMarker (  latitude.doubleValue(),
       
       
       
       
       
       
    longitude.doubleValue(),
       
       
       
       
       
       
    message );

               
listOfMarkers.add(marker);               

           
}
       
} catch ( SQLException sqle ){
           
sqle.printStackTrace();
       
} catch ( DataProviderException dpe ) {
           
dpe.printStackTrace();
        }
    }

The array of markers could also be saved in session scope.
Also, I hard-coded the center of the map in the prerender()
method:

    public void prerender() {
        //
Welcome to Versailles!
       
mapViewer1_center.setLatitude(48.802585);
       
mapViewer1_center.setLongitude(2.123336);
    }

The center is then centered on the MapPoint bound to the map’s center property (mapViewer1_center in our case).

The final part is to bind the map component markers
property to the newly created array of markers. Right-click on the map
component > “Property Binding…” and apply the binding
:

And voilà! All you need to do is RUN!
See this
tutorial
for more info on manipulating the AJAX map component.

Now you can use this with your own dataset (the one thing I really can
share is the data I used). Try with your contact list. My end-result of this mash-up is shown live here with
some statistics.

Author: alexismp

Google Developer Relations in Paris.