tag:blogger.com,1999:blog-19247510985020506322024-03-18T02:10:09.706-07:00Davy's Tech BlogA blog about technology, java, mobile development,....ddewaelehttp://www.blogger.com/profile/00193914878294828713noreply@blogger.comBlogger16125tag:blogger.com,1999:blog-1924751098502050632.post-22112317186821956982012-04-05T07:25:00.001-07:002012-04-07T13:05:44.307-07:00Node.js on Mac OS X - 30 minute guide<table>
<tr>
<td>
<p>
Today I decided to give <a href="http://nodejs.org" target="_blank">node.js</a> a try on Mac OS X Lion. Having no real hands-on experience in node.js, and wanting to get something end-to-end up and running fast, so I went off in search for a good hands on tutorial.
</p>
<p>
For those of you who don't know what node.js is : <i>"Node.js is a platform built on Chrome's JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices." </i>
</p>
<p>
I stumbled upon a <a href="https://github.com/indexzero/nodejs-intro" target="_blank">great nodejs intro</a> on github by <a href="http://blog.nodejitsu.com/" target="_blank">indexzero.</a> that gives you a nice end-to-end introduction to nodejs and <a href="http://couchdb.apache.org/" target="_blank">CouchDB</a>. The sample features a REST based api to perform CRUD operations on bookmarks. It uses CouchDB as a datastore.
</p>
</td>
<td><img src="http://dl.dropbox.com/u/13246619/Blog%20Articles/NodeJsLion/nodejsonmac.jpg" border="0"/></td>
</tr>
</table>
<p>
The goal of this post is to do the required setup in order to get the sample up and running under 30 minutes. I decided to give it a try and started by installing both node.js and CouchDB on my Mac OS X Lion. By the end of this tutorial, you'll have node.js and CouchDB installed, and will be able to run the REST based API on your OS X installation.
</p>
<p>This article assumes you have Git installed on your machine. If you don't have Git installed, checkout the <a href="http://help.github.com/mac-set-up-git/" target="_blank">Set Up Git on Mac</a> guide.
</p>
<h2>Installing node.js and npm</h2>
<p>
The easiest way to download and install node.js from the <a href="http://nodejs.org/#download" target="_blank">the nodejs download section</a> and pickup the Macintosh installer. It will install both node and the node package manager (npm).
</p>
<center><img src="http://dl.dropbox.com/u/13246619/Blog%20Articles/NodeJsLion/nodejsdownloads.JPG"/></center>
<p>
After the install, you'll have access to both the <b>node</b> and <b>npm</b> command.
</p>
<h2>Installing CouchDB</h2>
<p>
As the node js tutorial uses CouchDB to store objects, we'll need to install CouchDB.
</p>
<p>
Installing CouchDB is a bit more effort in the sense that we'll download the sources and compile them. Before we can do that, we first need to install Homebrew by executing the following commands :
</p>
<pre class="brush: java; toolbar: false;">
git clone https://github.com/mxcl/homebrew.git
cd homebrew/bin
brew install autoconf automake libtool
brew install couchdb
</pre>
<b>Important note ! : </b> There seems to be an issue with the CouchDB recipe introduced a couple of days ago that will prevent you from installing CouchDB. In order to fix this, you'll need to manually edit <b>~/couch/homebrew/Library/Formula/couchdb.rb</b>
<p>
Change
</p>
<pre>
require 'formula'
class Couchdb < Formula
url 'http://www.apache.org/dyn/closer.cgi?path=couchdb/source/1.1.1/apache-couchdb-1.1.1.tar.gz'
homepage "http://couchdb.apache.org/"
md5 'cd126219b9cb69a4c521abd6960807a6'
</pre>
<p>into this (notice how the "source folder" needs to be removed.</p>
<pre>
require 'formula'
class Couchdb < Formula
url 'http://www.apache.org/dyn/closer.cgi?path=couchdb/1.1.1/apache-couchdb-1.1.1.tar.gz'
homepage "http://couchdb.apache.org/"
md5 'cd126219b9cb69a4c521abd6960807a6'
</pre>
If the installation hangs, you can continue CTRL-C the install and try it again by executing
<pre class="brush: java; toolbar: false;">
./brew install -v couchdb
</pre>
More info on the process can be found in "<a href="http://wiki.apache.org/couchdb/Installing_on_OSX" target="_blank">Installing CouchDB on OSX</a>".
<p>
After the CouchDB compile is complete, you can start it by executing "./couchdb". You can verify that CouchDB is running by opening a browser and navigating to <a href="http://127.0.0.1:5984/_utils">http://127.0.0.1:5984/_utils</a>.
</p>
<center><img src="http://dl.dropbox.com/u/13246619/Blog%20Articles/NodeJsLion/futon.JPG"/></center>
<h2>Downloading the tutorial</h2>
<p>
Now that we have everything setup, we can continue with the node js intro sample.
</p>
<p>
We start by checking out the source.
</p>
<pre class="brush: java; toolbar: false;">
git clone https://github.com/indexzero/nodejs-intro.git
</pre>
<h2>Creating the CouchDB database</h2>
Before we can start the tutorial, we're going to have to create a CouchDB database/ We're going to be using the command line (make sure CouchDB is started) :
<pre class="brush: java; toolbar: false;">
$ curl -X PUT http://127.0.0.1:5984/pinpoint-dev10
{"ok":true}
</pre>
<p>
You can visit the CouchDB Futon http based console on <a href="http://127.0.0.1:5984/_utils">http://127.0.0.1:5984/_utils</a>. You should see your newly created database in the console.
</p>
<p>
There's an excellent CouchDB guide <a href="http://guide.couchdb.org/draft/tour.html" target="_blank">here</a>.
</p>
<h2>Starting the tutorial</h2>
<p>
The node js sample is constructed in a modular way. The lib folder contains several modules that can be bootstrapped using the server script in the bin folder. </p>
<p>
For example, to start the CouchDB tutorial, all you need to do is execute the following command from the bin folder
</p>
<pre class="brush: java; toolbar: false;">
./server -t 02couchdb -s
</pre>
<p>
The -t flag allows you to specify a module from the lib folder, the -s flag will setup the pinpoint-dev database we created earlier.
</p>
<h2>The sys - util change</h2>
<p>Depending on the version of Node that you're using you might see the following error or warning :</p>
<pre class="brush: java; toolbar: false;">
$ node -v
v0.7.7-pre
$ ./server -t 02couchdb -s
node.js:247
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: The "sys" module is now called "util".
at sys.js:1:69
at NativeModule.compile (node.js:572:5)
at Function.require (node.js:540:18)
at Function._load (module.js:297:25)
at Module.require (module.js:357:17)
at require (module.js:373:17)
at Object.<anonymous> (/home/ubuntu/nodejs-intro/bin/server:3:11)
at Module._compile (module.js:444:26)
at Object..js (module.js:462:10)
at Module.load (module.js:351:32)
</pre>
In order to get rid of the error, you'll need to replace all calls to `require("sys")` with `require("util")` in all the .js files you're using in your project (and its dependent modules).
<p>
Node v0.6.14 doesn't throw an error, but provides a warning that the package has been changed.
</p>
<pre class="brush: java; toolbar: false;">
$ node -v
v0.6.14
$ ./server -t 02couchdb -s
The "sys" module is now called "util". It should have a similar interface.
Pinpoint demo server listening for 02couchdb on http://127.0.0.1:8000
</pre>
<h2>Running the tutorial</h2>
<p>
When you run the tutorial, you're going to get some erros
</p>
<pre class="brush: java; toolbar: false;">
$ ./server 02couchdb
The "sys" module is now called "util". It should have a similar interface.
node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: Cannot find module 'optimist'
at Function._resolveFilename (module.js:332:11)
at Function._load (module.js:279:25)
at Module.require (module.js:354:17)
at require (module.js:370:17)
at Object.<anonymous> (/Users/ddewaele/Projects/Node/nodejs-intro/bin/server:5:12)
at Module._compile (module.js:441:26)
at Object..js (module.js:459:10)
at Module.load (module.js:348:31)
at Function._load (module.js:308:12)
at Array.0 (module.js:479:10)
</pre>
The tutorial has several dependencies that we'll need to download using the Node Package Manager (npm). The npm command comes with the nodejs installation on OS X.
<h2>Installing node packages</h2>
<p>
Node packages (dependencies) are installed using the npm like this :
</p>
<pre class="brush: java; toolbar: false;">
$ npm install optimist
npm http GET https://registry.npmjs.org/optimist
npm http 200 https://registry.npmjs.org/optimist
npm http GET https://registry.npmjs.org/optimist/-/optimist-0.2.8.tgz
npm http 200 https://registry.npmjs.org/optimist/-/optimist-0.2.8.tgz
npm http GET https://registry.npmjs.org/wordwrap
npm http 200 https://registry.npmjs.org/wordwrap
npm http GET https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz
npm http 200 https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz
optimist@0.2.8 ../node_modules/optimist
└── wordwrap@0.0.2
</pre>
<p>
The packages will be installed in a node_modules folder in the root folder of our tutorial :
<pre class="brush: java; toolbar: false;">
$ ls -l ../node_modules/
total 0
drwxr-xr-x 10 ddewaele staff 340 Apr 1 18:54 optimist
</pre>
<p>
the node js intro requires the following modules to be installed :
</p>
<pre class="brush: java; toolbar: false;">
npm install winston
npm install cradle
npm install journey
npm install optimist
</pre>
<h2>Running the tutorial</h2>
<p>
From the bin folder, start the tutorial by executing the following command :
</p>
<pre class="brush: java; toolbar: false;">
$ ./server -t 02couchdb -s
The "sys" module is now called "util". It should have a similar interface.
Pinpoint demo server listening for 02couchdb on http://127.0.0.1:8000
</pre>
<p>
Going to <a href="http://127.0.0.1:8000/bookmarks">http://127.0.0.1:8000/bookmarks</a> in a browser should return the following response :
</p>
<pre class="brush: java; toolbar: false;">
{"bookmarks":[]}
</pre>
This means our service is up and running. In order to add some data in CouchDB, we're going to be using http-console to access our REST service and send some data over the wire.
<h2>Installing http-console</h2>
<p>
A great tool to help you debug your services is something called http-console. You can install http-console through the node package manager. This time, we'll install the http-console package globally, making it available system-wide. We do this by using the -g (global) switch.
</p>
<pre class="brush: java; toolbar: false;">
sudo npm install -g http-console
</pre>
<p>
You'l notice that the http-console executable is now available on the command-line.
Unfortunately, when starting http-console you'll get the following error.
</p>
<pre class="brush: java; toolbar: false;">
$ http-console
node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: require.paths is removed. Use node_modules folders, or the NODE_PATH environment variable instead.
at Function.<anonymous> (module.js:378:11)
at Object.<anonymous> (/usr/local/lib/node_modules/http-console/bin/http-console:6:8)
at Module._compile (module.js:441:26)
at Object..js (module.js:459:10)
at Module.load (module.js:348:31)
at Function._load (module.js:308:12)
at Array.0 (module.js:479:10)
at EventEmitter._tickCallback (node.js:192:40)
</pre>
<p>
You can fix this by editing the /usr/local/lib/node_modules/http-console/bin/http-console file, and removing the following line :
</p>
<pre class="brush: java; toolbar: false;">
require.paths.unshift(path.join(__dirname, '..', 'lib'));
</pre>
<p>
After having removed the line above, http console should start. When providing no arguments, it will connect to http://localhost:8080 by default. If you want to connect to another server/port, just specify it as the first argument.
</p>
<p>
In order to connect to the server provided by the tutorial, we'll start the http-console like this (notice how we use the <b>\json</b> command to set the correct content-type):
</p>
<pre class="brush: java; toolbar: false;">
$ http-console http://127.0.0.1:8000
The "sys" module is now called "util". It should have a similar interface.
> http-console 0.6.1
> Welcome, enter .help if you're lost.
> Connecting to 127.0.0.1 on port 8000.
http://127.0.0.1:8000/> \json
http://127.0.0.1:8000/>
</pre>
<h2>Accessing the REST service</h2>
<p>
Inside the http-console, executing a GET request is as simple as typing <b>GET /bookmarks</b>. You should get the same response as the one you saw in your browser earlier :
</p>
<pre class="brush: java; toolbar: false;">
http://127.0.0.1:8000/> GET /bookmarks
HTTP/1.1 200 OK
Date: Sun, 01 Apr 2012 17:23:27 GMT
Server: journey/0.4.0
Content-Type: application/json;charset=utf-8
Content-Length: 16
Connection: keep-alive
{
bookmarks: []
}
</pre>
<p>
You can also execute POST commands like this by providing a JSON snippet.
</p>
<pre class="brush: java; toolbar: false;">
http://127.0.0.1:8000/> POST /bookmarks
... { "url": "http://nodejs.org" }
HTTP/1.1 200 OK
Date: Thu, 05 Apr 2012 11:45:55 GMT
Server: journey/0.4.0
Content-Type: application/json;charset=utf-8
Content-Length: 91
Connection: keep-alive
{
bookmark: {
_id: 'WD-G-1',
resource: 'Bookmark',
url: 'http://nodejs.org'
}
}
</pre>
<p>
When executing the GET request again, as you can see, our server responds with the bookmark that has now been inserted in CouchDB.
</p>
<pre class="brush: java; toolbar: false;">
http://127.0.0.1:8000/> GET /bookmarks
HTTP/1.1 200 OK
Date: Sun, 01 Apr 2012 17:23:27 GMT
Server: journey/0.4.0
Content-Type: application/json;charset=utf-8
Content-Length: 16
Connection: keep-alive
{
bookmarks: [
{
_rev: '1-cfced13a45a068e95daa04beff562360',
_id: 'WD-G-1',
resource: 'Bookmark',
url: 'http://nodejs.org'
}
]
}
</pre>
<h2>References</h2>
<ul>
<li><a href="http://nodejs.org" target="_blank">Node.js</a></li>
<li><a href="https://github.com/indexzero/nodejs-intro" target="_blank">Node.js intro git repository.</a></li>
<li><a href="http://blog.nodejitsu.com/a-simple-webservice-in-nodejs" target="_blank">Node.js intro blog post.</a></li>
<li><a href="https://github.com/mxcl/homebrew" target="_blank">Homebrew</a></li>
<li><a href="http://couchdb.apache.org/" target="_blank">CouchDB</a></li>
<li><a href="http://guide.couchdb.org/draft/tour.html" target="_blank">CouchDB tour</a></li>
<li><a href="http://wiki.apache.org/couchdb/Installing_on_OSX" target="_blank">Installing CouchDB on OSX</a></li>
</ul>ddewaelehttp://www.blogger.com/profile/00193914878294828713noreply@blogger.com1tag:blogger.com,1999:blog-1924751098502050632.post-60340141186705610142011-09-05T03:30:00.000-07:002011-09-05T04:13:17.355-07:00Android Market Horror Story<div dir="ltr" style="text-align: left;" trbidi="on">As a registered Android developer with <a href="https://market.android.com/details?id=com.ecs.latify&feature=search_result" target="_blank">a published application</a> on the Android Market, I'm becoming more amazed each day by how bad the Android Market team is supporting their developers.<br />
<br />
The relationship between developers and the Android Market has never been good, caused to a large degree by the many bugs and issues developers are facing on that Android Market. <br />
<br />
The reason for me writing this post was something that happened a couple of days ago on the Android Market. Have a look at the video below to see what happens when users try to search for my application :<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://i.ytimg.com/vi/B7zrlRXG_nA/0.jpg" height="399" width="480"><param name="movie" value="http://www.youtube.com/v/B7zrlRXG_nA?f=user_uploads&c=google-webdrive-0&app=youtube_gdata" /><param name="bgcolor" value="#FFFFFF" /><embed width="480" height="399" src="http://www.youtube.com/v/B7zrlRXG_nA?f=user_uploads&c=google-webdrive-0&app=youtube_gdata" type="application/x-shockwave-flash"></embed></object></div><br />
A couple of days before, a fellow developer contacted me that his sales dropped to almost zero since the new Android Market was released. When I went on the Market to search for his app, I noticed the following:<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='480' height='399' src='https://www.youtube.com/embed/Zz2uLRTMwcQ?feature=player_embedded' frameborder='0'></iframe></div><br />
<br />
It's not only end users who are facing serious issues in finding, downloading and purchasing applications, but developers are having a very difficult time in getting any kind of support from Google for the many issues they are facing.<br />
<br />
One should not forget that Google is taking 30% of all sales. Something that they are entitled to, and something I don't have any issue with whatsoever, but on the other hand, developers should expect some level of quality and support coming from the Android Market when working in such a model.<br />
<br />
A couple of weeks ago, Google has decided <a href="http://phandroid.com/2011/08/23/google-closing-android-market-developer-support-forums/" target="_blank">to close the Market Technical Forum for developers</a>, making it an end-user forum only. To quote Google, "<i>Because of the nature of developer issues, we feel that one-on-one support is best for the types of threads that have historically been posted to this forum by app developers."</i><br />
<br />
The Android Market forum was a place where developers could interact with each other on market specific issues, however, it seemed Google noticed that the forum was full of frustrated developers, making the Market look bad. Every once in a while, external sites like <a href="http://www.theregister.co.uk/2011/08/04/android_marketplace_payments/" target="_blank">The Register</a> started picking up stories from the forum, like when developers were not getting paid for a part of their app sales.<br />
<br />
Every once in awhile a Google employee posted something on the forum, mostly to state that they are <i>looking into the issue.</i>No due dates, no updates, effectively leaving developers all alone to rant about the various issues they are having. <br />
<br />
A forum might indeed not be the best way to deal with issues, but obviously a one-on-one support model also does not work in its current form. Either their support team is heavily under-staffed, or issues are given very low priority. I have the impression that Google is trying to hide these issues from the public instead of actually put effort in resolving them properly.<br />
<br />
Like me, many developers have logged tickets via email and don't get any response beyond "look at the known issues page", or worse, no response at all. This seems very strange for a service that is taking 30% of your profits. I wonder what kind of support big companies are getting when it comes to getting their issues resolved.<br />
<br />
Clearly, there is a need for support giving the many (sometimes serious) issues users and developers are facing with the Android Market. Just have a look at the known issues pages for both users and developers.<br />
<ul><li><a href="http://www.google.com/support/forum/p/Android+Market/thread?tid=296b9269d8a130dc&hl=en" target="_blank">Android Market for Developer Known Issues</a></li>
<li><a href="http://www.google.com/support/androidmarket/bin/static.py?page=known_issues.cs" target="_blank">Android Market Apps Known Issues</a></li>
</ul>The issues that stand out for me, that have been open for a long time, and are hurting independent developers are listed below.<br />
<ul><li><a href="http://www.phonearena.com/news/Missing-app-comments-on-the-Android-Market-draws-the-ire-of-developers_id20793" target="_blank">Missing reviews / ratings for applications</a></li>
<li>incorrect sorting of app comments in the Android Market</li>
<li>Developer console statistic issues (both stale and incorrect statistics)</li>
<li>inconsistent search results</li>
<li>3 separate market systems (mobile/tablet/web) with different behaviors</li>
<li><a href="http://www.readwriteweb.com/archives/android_market_developer_outage_continues_test_apps_show_up_in_wild.php?utm_source=twitterfeed&utm_medium=twitter" target="_blank">Outages on the Developer console</a></li>
</ul><br />
It's about time Google starts taking the Android Market seriously, but putting in place a decent support model, staffing the team appropriately and really start looking into resolving these issues. If not, I'm convinced that many developers will steer away from Android as a development platform.<br />
<br />
</div>ddewaelehttp://www.blogger.com/profile/00193914878294828713noreply@blogger.com3tag:blogger.com,1999:blog-1924751098502050632.post-42698145048632264362011-08-06T14:58:00.000-07:002011-08-08T13:44:13.529-07:00Google Latitude API and OAuth 2.0<div dir="ltr" style="text-align: left;" trbidi="on"><h2>Introduction</h2>In this article, I'm going to discuss <b>OAuth 2.0</b> and the <b>Latitude API</b>. A couple of days ago, <a href="https://groups.google.com/forum/#!topic/google-latitude-api/5ZQ1vd6aCSQ" target="_blank">OAuth 2.0 support was announced for the Latitude API</a>, and I figured it might be a good idea to take a more in-depth look on how OAuth 2.0 can be used with the Latitude API and how the <br />
<a href="http://code.google.com/p/google-api-java-client/" target="_blanlk">Google APIs Client Library for Java</a> implemented the Oauth 2.0 spec.<br />
<br />
The article is accompanied by <a href="https://github.com/ddewaele/LatitudeOAuth2Sample" target="_blank">a sample application on Github</a>. It's heavily inspired by existing samples for <b>Google APIs Client Library for Java</b>. In order to run the sample, clone the Git repo, import the project in Eclipse, fill in the Oauth constants (see subsequent section) and run the LatitudeSample class. In order to resolve the project dependencies, it's advised to use the <a href="http://www.eclipse.org/m2e/" target="_blank">Maven2 plugin for Eclipse</a>.<br />
<br />
The goal here is to start executing requests on the Latitude API, secured via OAuth 2.0. For this, we'll use a simple command line application. For the same flow using the Latitude API on the <b>Android</b> platform, checkout <a href="http://blog.doityourselfandroid.com/2011/08/06/oauth-2-0-flow-android" target="_blank">Oauth 2.0 flow in Android</a><br />
<br />
<a name='more'></a><br />
<br />
<h2>Project setup</h2>First, we need to have a look at our project setup. The project has various dependencies that are defined in the maven2 pom. I've made the code available in such a way that it can be directly imported in Eclipse.<br />
Providing you've installed the maven2 plugin for Eclipse, the plugin will automatically start downloading all dependencies required in order for you to run this project from the IDE.<br />
<br />
In short, the following maven2 dependencies are required :<br />
<br />
<pre class="brush: xml; toolbar: false;"><dependencies>
<dependency>
<groupid>com.google.api.client</groupid>
<artifactid>google-api-client-googleapis</artifactid>
<version>1.4.1-beta</version>
</dependency>
<dependency>
<groupid>com.google.api.client</groupid>
<artifactid>google-api-client</artifactid>
<version>1.4.1-beta</version>
</dependency>
<dependency>
<groupid>com.google.api.services.latitude</groupid>
<artifactid>google-api-services-latitude-v1</artifactid>
<version>1.1.0-beta</version>
</dependency>
<dependency>
<groupid>org.mortbay.jetty</groupid>
<artifactid>jetty</artifactid>
<version>6.1.26</version>
</dependency>
</dependencies>
</pre>Including the transitive dependencies, following is a list of all the libraries you'll need to run it. (as stated before, I highly recommend you using the maven2 plugin for eclipse in order to resolve these dependencies. It will get you up and running really fast).<br />
<br />
M2_REPO/commons-codec/commons-codec/1.3/commons-codec-1.3.jar<br />
M2_REPO/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.jar<br />
M2_REPO/com/google/api/client/google-api-client/1.4.1-beta/google-api-client-1.4.1-beta.jar <br />
M2_REPO/com/google/api/client/google-api-client-googleapis/1.4.1-beta/google-api-client-googleapis-1.4.1-beta.jar <br />
M2_REPO/com/google/api/services/latitude/google-api-services-latitude-v1/1.1.0-beta/google-api-services-latitude-v1-1.1.0-beta.jar <br />
M2_REPO/com/google/code/gson/gson/1.6/gson-1.6.jar <br />
M2_REPO/com/google/guava/guava/r09/guava-r09.jar <br />
M2_REPO/org/apache/httpcomponents/httpclient/4.0.3/httpclient-4.0.3.jar <br />
M2_REPO/org/apache/httpcomponents/httpcore/4.0.1/httpcore-4.0.1.jar <br />
M2_REPO/org/codehaus/jackson/jackson-core-asl/1.6.7/jackson-core-asl-1.6.7.jar <br />
M2_REPO/org/mortbay/jetty/jetty-util/6.1.26/jetty-util-6.1.26.jar<br />
M2_REPO/org/mortbay/jetty/jetty/6.1.26/jetty-6.1.26.jar<br />
M2_REPO/com/google/code/findbugs/jsr305/1.3.9/jsr305-1.3.9.jar/><br />
M2_REPO/junit/junit/4.8.2/junit-4.8.2.jar<br />
M2_REPO/org/mortbay/jetty/servlet-api/2.5-20081211/servlet-api-2.5-20081211.jar<br />
M2_REPO/xpp3/xpp3/1.1.4c/xpp3-1.1.4c.jar<br />
<br />
If all went well, you should see the following in your Eclipse:<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjK4h4DgOqwkG4tJNqqP2nvulXJBWQhEuR17CQodAaF_3Ej52m0rUC2EePXDj2D01gYLYqGzo_v7BvQkHsrkLKAjogo2EvxdEfLdXxonswHrrfOesvIRPwOOYrigkhRhGu0hZK0pH5R7i_w/s1600/project_setup.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="385" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjK4h4DgOqwkG4tJNqqP2nvulXJBWQhEuR17CQodAaF_3Ej52m0rUC2EePXDj2D01gYLYqGzo_v7BvQkHsrkLKAjogo2EvxdEfLdXxonswHrrfOesvIRPwOOYrigkhRhGu0hZK0pH5R7i_w/s400/project_setup.PNG" width="377" /></a></div><br />
<br />
<br />
<h2>OAuth 2 setup</h2>Before you can run the application, you'll need to get a hold of an OAuth client ID, and an API key for Google Latitude. Both things can be retrieved from the <a href="http://code.google.com/apis/console" target="_blank">Google API console</a>. The Google API console allows you to define one or more projects. Create a project, and make sure that for "Latitude API", the status switch is flipped to the "ON" position.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9SSYSXhlcXoIux5VS6Q5Vswi9bgak2QtMatz85LeULmppSB0MXBZAMb3eS09hYfT6bBhL-91w05BmVqS50qO0wwWuU_v02oKonCpybu_4ZmiGbhaF52VoDvYN_m_hVyucvDYZHUNf-RPX/s1600/latitude_enabled.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="383" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9SSYSXhlcXoIux5VS6Q5Vswi9bgak2QtMatz85LeULmppSB0MXBZAMb3eS09hYfT6bBhL-91w05BmVqS50qO0wwWuU_v02oKonCpybu_4ZmiGbhaF52VoDvYN_m_hVyucvDYZHUNf-RPX/s400/latitude_enabled.PNG" width="400" /></a></div><br />
Next, click on "API Access". You should see the following screen : <br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgk8SS66vGZ-em0g_vGalA48o40PT8NIVi5DwqghwD9cXyEwdYGx8UDqWEJkmyLhquHJkY8D6EsMOqAu5_BZK8IOvPlmuWdR8J9zrK2itNdXWQPimko2_0t5NFrQDhpEjGnAQPgD1IUwOkD/s1600/console1.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="292" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgk8SS66vGZ-em0g_vGalA48o40PT8NIVi5DwqghwD9cXyEwdYGx8UDqWEJkmyLhquHJkY8D6EsMOqAu5_BZK8IOvPlmuWdR8J9zrK2itNdXWQPimko2_0t5NFrQDhpEjGnAQPgD1IUwOkD/s400/console1.PNG" width="400" /></a></div><br />
Click on "Create an OAuth 2.0 Client ID...". Choose a product name, and optionally load up an image and click "Next". (the product name and image will be shown to the user when your application is requesting him to authorize access).<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjz8_0y9wS-QCWhqn8JyxcPuDs3ccyfUdwGcRk-eCvEdz_x0JCRXv20qpf9kZCKXRkD_wUZ6Wx7ifmRS-LTkCcVibzSNIrWh1EPS9-9WZcW_Zlql93bU0bTzE7rFSkQkppY3RP1hrjZshTN/s1600/console3.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="296" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjz8_0y9wS-QCWhqn8JyxcPuDs3ccyfUdwGcRk-eCvEdz_x0JCRXv20qpf9kZCKXRkD_wUZ6Wx7ifmRS-LTkCcVibzSNIrWh1EPS9-9WZcW_Zlql93bU0bTzE7rFSkQkppY3RP1hrjZshTN/s400/console3.PNG" width="400" /></a></div><br />
Make sure you select "Installed application" and click "Create client ID".<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9ox3xppw2SM90vKivxJlyBYXdtnfQ7sU4_RZZbHvPdmW6Q_jUsRYrc8RYI1tz8dmuAtlHtmY48vAJIa3gtzz0y7st0PjXCBGuCW5yHJR_KW8RBkjv2QqOUWLqMBL6z4ym1XM3Jvn9hlQ3/s1600/console-installed-app.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="222" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9ox3xppw2SM90vKivxJlyBYXdtnfQ7sU4_RZZbHvPdmW6Q_jUsRYrc8RYI1tz8dmuAtlHtmY48vAJIa3gtzz0y7st0PjXCBGuCW5yHJR_KW8RBkjv2QqOUWLqMBL6z4ym1XM3Jvn9hlQ3/s400/console-installed-app.PNG" width="400" /></a></div><br />
You should see the following summary<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjf3DonswO6dAzFlrUW03Q6D42mkrzP-1NHq4RRwtpptjzHdFMIeMUslu4Z7nIPwWFzvpOvgKQWtnqPvG4_6o1_n-_78X0I0G8nrhAtVC1S_YXDOYo_DrLpTyx2_pEzgDVHXmLWtMSREoqt/s1600/console-summary.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjf3DonswO6dAzFlrUW03Q6D42mkrzP-1NHq4RRwtpptjzHdFMIeMUslu4Z7nIPwWFzvpOvgKQWtnqPvG4_6o1_n-_78X0I0G8nrhAtVC1S_YXDOYo_DrLpTyx2_pEzgDVHXmLWtMSREoqt/s400/console-summary.PNG" width="389" /></a></div><br />
<br />
<br />
This summary contains 2 important items<br />
<br />
<ul><li>The OAuth Client ID / Secret</li>
<li>The API key</li>
</ul><br />
You need the Oauth Client ID to setup your Oauth 2.0 communication. The API key is used to track the Latitude API usage in the console when clicking on the <b>Reports</b> link.<br />
<br />
The sample code contains a <a href="https://github.com/ddewaele/LatitudeOAuth2Sample/blob/master/src/main/java/com/ecs/sample/oauth2/latitude/cmdline/OAuth2ClientCredentials.java" target="_blank">OAuth2ClientCredentials class</a> where these constants can be defined.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPPDPb_p_O0MDD_5GOJiV7CgKw-yucelp9frDP20Ed8KXuj15cR8BlyJQR3pKD6yawwkIlQaTw4NDhCPqXaKuAsolqLQJ0gQ7h2Acx2m-_GctuUyMCtAOx-jU6SQVyjX5zAGoRPs-P6O16/s1600/latitude_api_usage.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="159" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPPDPb_p_O0MDD_5GOJiV7CgKw-yucelp9frDP20Ed8KXuj15cR8BlyJQR3pKD6yawwkIlQaTw4NDhCPqXaKuAsolqLQJ0gQ7h2Acx2m-_GctuUyMCtAOx-jU6SQVyjX5zAGoRPs-P6O16/s400/latitude_api_usage.PNG" width="400" /></a></div><br />
<h2>OAuth 2.0 web flow</h2>The OAuth 2.0 web based flow that we'll be using to authorize our API calls requires 2 important things : <br />
<ul><li>a user interacting with the browser to authorize access to a certain API (in this case Latitude).</li>
<li>our application capturing the response once the user authorized the request. (to capture the code)</li>
</ul>In order to successfully access the Latitude API, we need to have an access key. An access key can be made available to the application after the user has granted you (or your application) access through the OAuth 2.0 web flow.<br />
Subsequent calls to the API can re-use this access token. Although the access token is short-lived (expires after 1 hour), it needs to be "refreshed" using a refresh token. Luckily for us, the google api client for java does the refreshing for us.<br />
<br />
When starting the application for the first time, we obviously don't have an access token yet.<br />
We need to pop a browser from our command line. There are very few real-world use-cases that would work this way (popping a browser from a command line), but it's done to explain the mechanics of OAuth 2.0. <br />
<br />
I'll post a sample soon on how this can be done from a web application or an android application.<br />
<br />
<h2>Letting the user authorize access</h2>The user does so through a web page provided by Google. Our application needs to redirect the user to an authorization URL that can be generated using the <br />
<br />
<pre class="brush: java; toolbar: false;">String authorizationUrl =
new GoogleAuthorizationRequestUrl(clientId, redirectUrl, scope).build();
</pre>The generated URL that looks like this:<br />
<br />
https://accounts.google.com/o/oauth2/auth?client_id=1021231231376.apps.googleusercontent.com&redirect_uri=http://localhost:51871/Callback&response_type=code&scope=https://www.googleapis.com/auth/latitude.all.best<br />
<br />
The page looks like this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1_71yqUILKuQYiJZoIMrDXWh0x4xDjX7HmbPfhsBw5pgJoaHeiT_V1SKMimYiLlYC0f_2CoigFwHmY3FUJGiSP9wm8fsUszeMBkZf5rv6BaAsF-IOOCnkZbxx6TNkwvBZRf0CytZtZaT_/s1600/authorize_page.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="215" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1_71yqUILKuQYiJZoIMrDXWh0x4xDjX7HmbPfhsBw5pgJoaHeiT_V1SKMimYiLlYC0f_2CoigFwHmY3FUJGiSP9wm8fsUszeMBkZf5rv6BaAsF-IOOCnkZbxx6TNkwvBZRf0CytZtZaT_/s400/authorize_page.PNG" width="400" /></a></div><br />
The sample application will launch a browser displaying that page, and will wait untill the user authorizes acces. (For more details on how this is done, check the exchangeCodeForAccessToken method in the OAuth2Native class).<br />
<br />
As you can see, it contains the following parameters<br />
<br />
<b>client_id</b><br />
The client ID as found on the Google API Console<br />
<br />
<b>redirect_uri</b><br />
A URL where Google will redirect us to after the user has authorized access<br />
<br />
<b>response_type</b><br />
<br />
<b>scope</b><br />
The scope that we want to use for our API calls. As we're using Latitude, we use the <a href="http://code.google.com/apis/latitude/v1/using_rest.html#auth" target="_blank">new OAuth 2.0 scopes defined by the Latitude API</a>.<br />
<br />
When the user authorizes access, a code is sent as a request parameter in the redirect URI that we've specified. As our sample application has launched an embedded Jetty running on our localhost, we can capture the code in the request parameter.<br />
<br />
Once we've captured thus code, we can now go and initialize a <b>GoogleAuthorizationCodeGrant</b> (Google extension to the OAuth 2.0 Web Server Flow), to exchange this code for an actual access token, required to perform API calls.<br />
<br />
When the <b>GoogleAuthorizationCodeGrant</b> is executed, it returns an <b>AccessTokenResponse</b>, where our OAuth 2.0 access token (and refresh token) is encapsulated.<br />
(Internally, the GoogleAuthorizationCodeGrant will go on to request the actual access token by executing a HTTP POST to https://accounts.google.com/o/oauth2/token)<br />
The API takes care of processing the response, and returning it as an AccessTokenResponse object.<br />
<br />
Typical usage of the code looks like this :<br />
<br />
<pre class="brush: java; toolbar: false;">GoogleAuthorizationCodeGrant request =
new GoogleAuthorizationCodeGrant(new NetHttpTransport(),
new JacksonFactory(),
"s6BhdRkqt3",
"gX1fBat3bV",
"i1WsRn1uB1",
"https://client.example.com/cb");
AccessTokenResponse response = request.execute();
System.out.println("Access token: " + response.accessToken);
</pre><br />
An accessTokenResponse looks like this :<br />
<br />
{access_token=1/BlrDfsdfsdfsdfsdfTpnbWz_9dqEZyPZ-g2jM, expires_in=3600, refresh_token=1/D36E7QYxLWpnsdfsfsdf1MM2lgRTpxYxlmw, token_type=Bearer}<br />
<br />
Keep in mind that once you've retrieved an access token, you can store it somewhere safely in your application. You obviously don't want to have the user authorize each and every individual API call. The sample application is shipped with <a href="https://github.com/ddewaele/LatitudeOAuth2Sample/blob/master/src/main/java/com/ecs/sample/oauth2/latitude/cmdline/oauth/FileBasedCredentialStore.java" target="_blank">a very simple FileBasedCredentialStore</a>, storing the access token data in a file on the filesystem, and reading it from there when required.<br />
<br />
A file called latitude-accesstoken will be stored in your temp folder, containing the access token, expiration and refresh token.:<br />
<br />
<pre>1/Hdbvdb7ydsfsdfdfZw2zFLRfeYCxr2zuSuc
3600
1/PjzuhTEyu6sdfdsfsdfI1fafW_QE6PqeVWlBw
</pre><br />
<br />
Once an access token is retrieved, we can re-use it, and start creating a <b>GoogleAccessProtectedResource</b>.<br />
<br />
<pre class="brush: java; toolbar: false;">return new GoogleAccessProtectedResource(response.accessToken,
transport,
jsonFactory,
clientId,
clientSecret,
response.refreshToken)
</pre><br />
the <b>GoogleAccessProtectedResource</b> is a key component here, as this is what we'll use to initialize the Latitude service. This is actually a requestInitializer, a part of the <a href="http://code.google.com/p/google-api-java-client/" target="_blanlk">Google APIs Client Library for Java</a>, and used <br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiO07qLy9KYvcST3HQzCg49HCcQ4FStJNMfkkCCT2DZytfMi8CSiQn9_CJsT3ogZx2R5sw22YLNbg8N6zQ9o042s4DjbGYMqt3Mbl1lrpfGGn0Dlj6QPqCME_f-sZ4bEr4wz-b04jQ9fEX6/s1600/httprequestinitializer.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="189" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiO07qLy9KYvcST3HQzCg49HCcQ4FStJNMfkkCCT2DZytfMi8CSiQn9_CJsT3ogZx2R5sw22YLNbg8N6zQ9o042s4DjbGYMqt3Mbl1lrpfGGn0Dlj6QPqCME_f-sZ4bEr4wz-b04jQ9fEX6/s400/httprequestinitializer.PNG" width="280" /></a></div><br />
<h2>The Latitude Service definition</h2>Up until now, we've only covered the OAuth 2.0 part. Rest assured, we have the most complex part out of the way now, and assuming we have succesfully retrieved an access token, we can start using the Latityde API. <br />
<br />
<a href="http://code.google.com/p/google-api-java-client/" target="_blanlk">Google APIs Client Library for Java</a> offers <a href="http://code.google.com/p/google-api-java-client/wiki/APILibraries#Google_Latitude_API">a client library for the Latitude API</a>. It contains the Latitude model classes, as well as the bulk of the plumbing code to perform Latitude API calls.<br />
<br />
A central component in the Latitude API is the Latitude service definition, represented by the Latitude object. The Latitude object is initialized like this;<br />
<br />
<pre class="brush: java; toolbar: false;">final Latitude latitude = new Latitude(transport, accessProtectedResource, jsonFactory);
</pre><br />
As you can see, the Latitude service definition is initialized with:<br />
<br />
<b>transport</b><br />
The HTTP transport used to perform the REST calls.<br />
<br />
<b>accessProtectedResource</b><br />
See previous section. Available after having retrieved the access token. Encapsulates all the OAuth2.0 stuff, required for authorized access to the Latitude API<br />
<br />
<b>jsonFactory</b><br />
Factory required to parse the JSON based HTTP responses <br />
<br />
The Latitude service definition also has other properties you can configure.<br />
<br />
<pre class="brush: java; toolbar: false;">latitude.apiKey="AsdfdsfsdfdftFhVABVWuFk";
latitude.setApplicationName("Google-LatitudeSample/1.0");
latitude.prettyPrint = true;
</pre><br />
The Latitude service definition encapsulates all Latitude specific stuff like the endpoint (https://www.googleapis.com/latitude/v1/) and the various operations on the service.<br />
<br />
Now that our service definition is properly setup, we can use it to make the actual API calls<br />
<br />
<h2>Performing API calls</h2><br />
The Latitude service definition exposes various operations (currentLocation and location). On each of these operations, RemoteRequests are defined (Get / Delete / Insert).<br />
On the service definition, we get a reference to the operation (currentlocation), and then get a reference to the RemoteRequest (Get). <br />
First, we're going to retrieve the current location of the user. <br />
<h3>Retrieving the current location</h3>To show the current location, we execute the following method :<br />
<br />
<pre class="brush: java; toolbar: false;">static void showCurrentLocation(Latitude latitude) throws IOException {
View.header("Show CurrentLocation ");
LatitudeCurrentlocationResourceJson latitudeCurrentlocationResourceJson = latitude.currentLocation.get().execute();
View.display(latitudeCurrentlocationResourceJson);
}
</pre><br />
[insert image]<br />
<br />
The Latify service definition does an excellent job of encapsulating <br />
<br />
<ul><li>building the lower level HTTP requests.</li>
<li>constructing the base URLs, makes sure our API key is properly appended</li>
<li>authenticating our request.</li>
<li>executing the HTTP calles</li>
<li>handling responses</li>
</ul><br />
All it takes is one simple line of code to retrieve the current location.<br />
<br />
<h3>Updating the current location</h3><br />
Updating your location is equally simple : <br />
<br />
<pre class="brush: java; toolbar: false;">static void updateCurrentLocation(Latitude latitude) throws IOException {
View.header("Show CurrentLocation ");
LatitudeCurrentlocationResourceJson latitudeCurrentlocationResourceJson = new LatitudeCurrentlocationResourceJson();
latitudeCurrentlocationResourceJson.put("latitude", "32.40");
latitudeCurrentlocationResourceJson.put("longitude", "12.40");
LatitudeCurrentlocationResourceJson response = latitude.currentLocation
.insert(latitudeCurrentlocationResourceJson).execute();
View.display(response);
}
</pre><br />
<h4>Showing location history</h4><br />
As is showing your location history<br />
<br />
<pre class="brush: java; toolbar: false;">static void showLocationHistory(Latitude latitude) throws IOException {
View.header("Show CurrentLocation ");
com.google.api.services.latitude.Latitude.Location.List list = latitude.location.list();
list.maxResults = "10";
LocationFeed locationFeed = list.execute();
List<location> locations = locationFeed.items;
for (Location location : locations) {
View.display(location);
}
}
</location></pre><br />
when it is about to execute the HTTP GET request for retrieving the current location, the Latitude service definition will intercept that call, and make sure the OAuth token is properly added to the HTTP authorization header.<br />
<br />
<h2>Conclusions</h2>It's nice to see that the Latitude API starts supporting OAuth 2.0. It was a popular feature request and it's great to see that Google delivered. Although OAuth 2.0 is less complex when compared to OAuth 1.0, it does require you to have a basic knowledge of OAuth, and the various steps in the OAuth flow. <br />
<br />
Luckily, many client APIs have OAuth 2.0 support built-in, making it more easy for developers to write third party applications, focussing on business logic, and not worrying about the lower-level plumbing. <br />
<br />
It's still unclear how platforms like Android will deal with OAuth 2.0. Android developers can off course do the standard web based flow like we outlined here, but I always felt it was one of the shortcomings of Oauth 1.0, and having the user leave your application to push a button on a web-page doesn't make for a very user-friendly experience. There were plans to hook up the Android AccountManager with OAuth 2.0, but I didn't manage to get this up & running with the Latitude API (It did work when using Google Buzz / Tasks API).<br />
<br />
<br />
<br />
<h2>References</h2><ul><li><a href="http://code.google.com/p/google-api-java-client/" target="_blank">Google APIs Client Library for Java</a></li>
<li><a href="http://code.google.com/p/google-api-java-client/wiki/APILibraries#Google_Latitude_API">Google Latitude API client library</a></li>
<li><a href="http://code.google.com/apis/latitude/" target="_blank">Google Latitude API </a></li>
<li><a href="http://code.google.com/apis/console" target="_blank">Google API console</a></li>
<li><a href="http://code.google.com/apis/accounts/docs/OAuth2.html" target="_blank">Google OAuth2</a></li>
<li><a href="http://www.eclipse.org/m2e/" target="_blank">Maven2 plugin for Eclipse</a></li>
<li><a href="http://blog.doityourselfandroid.com/2011/08/06/oauth-2-0-flow-android" target="_blank">Oauth 2.0 flow in Android</a></li>
</ul></div>ddewaelehttp://www.blogger.com/profile/00193914878294828713noreply@blogger.com1tag:blogger.com,1999:blog-1924751098502050632.post-82836556407558441442011-07-18T23:25:00.000-07:002011-07-19T01:02:38.131-07:00Accessing the Google Public Location Badge programmatically<h1>Introduction</h1>The Google Public Location Badge provides a convenient way to share your Latitude location with others. The <a href="https://www.google.com/latitude/b/0/apps" target="_blank">Google Latitude apps page </a> allows you to enable your Public Location Badge, and provides you with an HTML snippet that you can add to your blog or website, where it will render a Google Map.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhabCySF9pWoZChDzWQXStFsHe2Yb1V8XuuQEbzE4wFb0wJnfdNXEPW-lYk6ewFokL5YaOcMJUtHAXlsvY3JLzVyDk5UufMBFa18tcsVwk2QjhAg3NllSm5B8r08cMSLhK6Daykn90BISnk/s1600/public_location_badge.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="244" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhabCySF9pWoZChDzWQXStFsHe2Yb1V8XuuQEbzE4wFb0wJnfdNXEPW-lYk6ewFokL5YaOcMJUtHAXlsvY3JLzVyDk5UufMBFa18tcsVwk2QjhAg3NllSm5B8r08cMSLhK6Daykn90BISnk/s640/public_location_badge.JPG" width="640" /></a></div><br />
<h1>JSON responses</h1>Another way of accessing your location through the Public Location Badge is by using JSON. This allows for a more programmatic access of your location that you can embed in your applications.<br />
<br />
This method is very handy if you want to access the current location of someone who has exposed his location through the Public badge.<br />
<br />
Each public location badge is associated with a userid. This numeric value is associated with each google account.<br />
<br />
If you want to access the JSON string, you can type the following URL in your browser.<br />
<br />
<pre class="brush: java; toolbar: false;">http://www.google.com/latitude/apps/badge/api?user=3356651955207924992&type=json
</pre><br />
It should return a string like this :<br />
<br />
<pre class="brush: java; toolbar: false;">{"type": "FeatureCollection", "features": [
{ "type": "Feature",
"geometry": {"type": "Point", "coordinates": [4.47658, 51.02511]},
"properties": {
"id": "3356651955207924992",
"accuracyInMeters": 0,
"timeStamp": 1311050105,
"reverseGeocode": "Arrondissement of Mechelen, Belgium",
"photoUrl": "http://www.google.com/latitude/apps/badge/api?type=photo&photo=gJsAQTEBAAA.rGdvZsJpNd7LFS7MT5g_Bg.YtjvkPGuqXl4iqhY7QGOyg",
"photoWidth": 96,
"photoHeight": 96,
"placardUrl": "http://www.google.com/latitude/apps/badge/api?type=photo_placard&photo=gJsAQTEBAAA.rGdvZsJpNd7LFS7MT5g_Bg.YtjvkPGuqXl4iqhY7QGOyg&moving=true&stale=true&lod=4&format=png",
"placardWidth": 56,
"placardHeight": 59
}
}
]
}
</pre><h1>Java access</h1>We can use the <a href="http://code.google.com/p/google-api-java-client/" target="_blank">Google APIs Client Library for Java</a> to access your public location badge from a java application. It provides a simple an elegant way to make the REST call, and transform the JSON string into a java based model.<br />
<br />
The sample application I'll be showing here is very simple, and is made up of 1 java class that contains a main method.<br />
<br />
We start by creating our HttpTransport and requestfactory, required to perform the REST call.<br />
<br />
<pre class="brush: java; toolbar: false;">private static final HttpTransport transport = new ApacheHttpTransport();
private static final String PUBLIC_BADGE_URL = "http://www.google.com/latitude/apps/badge/api";
public static HttpRequestFactory createRequestFactory(final HttpTransport transport) {
return transport.createRequestFactory(new HttpRequestInitializer() {
public void initialize(HttpRequest request) {
GoogleHeaders headers = new GoogleHeaders();
headers.setApplicationName("Google-Latitue-Public-Badge-Sample");
request.headers=headers;
JsonHttpParser parser = new JsonHttpParser();
parser.jsonFactory = new JacksonFactory();
request.addParser(parser);
}
});
} </pre><pre class="brush: java; toolbar: false;"></pre><br />
<h1>The Java model</h1>We also need to model the JSON string into java. As we're interested in the users location, the simplest model I could come up with looks like this :<br />
<br />
<pre class="brush: java; toolbar: false;">public static class PublicBadge {
@Key
public Feature[] features;
@Key
public String type;
public static class Feature {
@Key
public Geometry geometry;
}
public static class Geometry {
@Key
public float[] coordinates;
}
}
</pre><br />
<h1>Performing the REST call</h1>What's left now is to perform the actual call, and parse the response into our java model.<br />
<br />
<pre class="brush: java; toolbar: false;">public static void main(String[] args) throws Exception {
HttpRequestFactory httpRequestFactory = createRequestFactory(transport);
HttpRequest request = httpRequestFactory.buildGetRequest(new GenericUrl(PUBLIC_BADGE_URL));
request.url.put("user", "INSERT USER ID HERE");
request.url.put("type", "json");
//System.out.println(request.execute().parseAsString());
PublicBadge publicBadge = request.execute().parseAs(PublicBadge.class);
System.out.println("Longitude " + publicBadge.features[0].geometry.coordinates[0]);
System.out.println("Latitude " + publicBadge.features[0].geometry.coordinates[1]);
}
</pre><br />
<h1>Project setup</h1>You can copy paste these bits of code into a single java class. In order to use the <br />
<a href="http://code.google.com/p/google-api-java-client/" target="_blank">Google APIs Client Library for Java</a>, you need to have the following dependencies in place :<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJPTUHSdwn7Z6xKfwGssSfl2tZ5oj9nzbr3J925fLg0fL1rEt_n0Llgm8xBj_9DSnM1UsYFJw_kbSycD3aRnNU1IdBJW6TFHgvk65y8oLrVcEHKUVg6eKCf48cTgUPfksz5S1znrKXfq1l/s1600/public_badge_project_layout.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJPTUHSdwn7Z6xKfwGssSfl2tZ5oj9nzbr3J925fLg0fL1rEt_n0Llgm8xBj_9DSnM1UsYFJw_kbSycD3aRnNU1IdBJW6TFHgvk65y8oLrVcEHKUVg6eKCf48cTgUPfksz5S1znrKXfq1l/s320/public_badge_project_layout.JPG" width="281" /></a></div><br />
<br />
The easiest way to do that is to define the <a href="http://code.google.com/p/google-api-java-client/" target="_blank">Google APIs Client Library for Java</a> dependencies in a pom.xml, and use maven to resolve the dependencies for you. The pom.xml to have this sample up and running looks like this :<br />
<br />
<pre class="brush: xml; toolbar: false;"><project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelversion>4.0.0</modelversion>
<parent>
<groupid>com.google</groupid>
<artifactid>google</artifactid>
<version>5</version>
</parent>
<groupid>com.ecs.latitude</groupid>
<artifactid>publicbadge</artifactid>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<artifactid>maven-compiler-plugin</artifactid>
<configuration>
<source></source>1.6
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupid>com.google.api.client</groupid>
<artifactid>google-api-client</artifactid>
<version>1.4.1-beta</version>
</dependency>
<dependency>
<groupid>com.google.api.client</groupid>
<artifactid>google-api-client-googleapis</artifactid>
<version>1.4.1-beta</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>google-api-services</id>
<url>http://mavenrepo.google-api-java-client.googlecode.com/hg</url>
</repository>
</repositories>
<properties>
<project.build.sourceencoding>UTF-8</project.build.sourceencoding>
</properties>
</project>
</pre><br />
The sample app will simply output the latitude / longitude associated with the Public Badge.<br />
<br />
<pre>Latitude 4.47658
Longitude 51.02511
</pre><br />
<h1>References</h1><ul><li><a href="https://www.google.com/latitude/b/0/apps">Google Latitude apps</a></li>
<li><a href="http://code.google.com/p/google-api-java-client/" target="_blank">Google APIs Client Library for Java</a></li>
<li><a href="http://maven.apache.org/">Apache Maven</a></li>
<li><a href="http://eclipse.org/m2e/">Maven Plugin for Eclipse</a></li>
</ul>ddewaelehttp://www.blogger.com/profile/00193914878294828713noreply@blogger.com1tag:blogger.com,1999:blog-1924751098502050632.post-77067249467902897552011-07-11T13:53:00.000-07:002011-07-11T15:04:09.385-07:00Samsung Galaxy S GPS issues after Gingerbread upgradeAfter upgrading my Samsung Galaxy S to Gingerbread, I was having some serious GPS issues. <br />
The unability to get a location fix in a timely fashion, and the many drops during location requests were driving me crazy. <br />
Screenshots below show my daily commute. Location fixes were requested every 30 seconds. You can clearly see the difference before and after the fix. <br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-XIX5jhB0ff8/ThsgpdYCDDI/AAAAAAAABtE/97NDOEgWRzk/s1600/before_patch.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="http://2.bp.blogspot.com/-XIX5jhB0ff8/ThsgpdYCDDI/AAAAAAAABtE/97NDOEgWRzk/s400/before_patch.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Before the fix</td></tr>
</tbody></table><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-Y_p9dRYzxUs/ThsgvuoFkiI/AAAAAAAABtI/_Bee-yklrfs/s1600/after_patch.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="http://1.bp.blogspot.com/-Y_p9dRYzxUs/ThsgvuoFkiI/AAAAAAAABtI/_Bee-yklrfs/s400/after_patch.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">After the fix<br />
<br />
</td></tr>
</tbody></table><br />
<div style="text-align: left;">How it got fixed ? By "patching" the GPS receiver with a simple piece of paper....<br />
<div style="text-align: left;"><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-size: small;">It seems to do the trick, but I'll need to do some more field testing to be sure. </span><br />
<span class="Apple-style-span" style="font-size: small;"><br />
</span><br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-XzJAGnueVII/ThsiSYdCIJI/AAAAAAAABtc/5N_9Du37aO8/s1600/gps_patch1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="267" src="http://3.bp.blogspot.com/-XzJAGnueVII/ThsiSYdCIJI/AAAAAAAABtc/5N_9Du37aO8/s400/gps_patch1.jpg" width="400" /></a></div><span class="Apple-style-span" style="font-size: small;"><br />
</span><br />
<span class="Apple-style-span" style="font-size: small;"><br />
</span><br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-Np5Ft5pXzkA/ThsiSjnpYUI/AAAAAAAABtY/-Og-FBRQl6o/s1600/gps_patch2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="267" src="http://3.bp.blogspot.com/-Np5Ft5pXzkA/ThsiSjnpYUI/AAAAAAAABtY/-Og-FBRQl6o/s400/gps_patch2.jpg" width="400" /></a></div><span class="Apple-style-span" style="font-size: small;"><br />
</span><br />
<span class="Apple-style-span" style="font-size: small;"><br />
</span><br />
I found the possible solution (that I didn't really believe at first) at the <a href="http://www.androidworld.nl/forum">Dutch AndroidWorld forum</a>.</div></div><div style="text-align: left;"><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-size: small;">For more details on the solution, check the <a href="http://forum.xda-developers.com/showthread.php?t=1043034">XDA Developers forum</a>.</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><br />
</div></div><br />
</div><br />
<div class="separator" style="clear: both; text-align: center;"></div><br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"></div>ddewaelehttp://www.blogger.com/profile/00193914878294828713noreply@blogger.com0tag:blogger.com,1999:blog-1924751098502050632.post-70832480518556384422011-05-23T12:25:00.000-07:002011-07-21T11:49:28.162-07:00Introducing the Google Places API<div dir="ltr" style="text-align: left;" trbidi="on"><h2><span class="Apple-style-span" style="font-size: small; font-weight: normal;">A couple of weeks ago, at </span><span class="Apple-style-span" style="font-size: small; font-weight: normal;"><a href="http://www.google.com/events/io/2011/index-live.html" targe="_blank">the Google I/O developer conference</a></span><span class="Apple-style-span" style="font-size: small; font-weight: normal;"> in San Francisco, Google announced the opening up and general availability of the </span><span class="Apple-style-span" style="font-size: small; font-weight: normal;"><a href="http://code.google.com/intl/nl/apis/maps/documentation/places/" target="_blank">Google Places API</a></span><span class="Apple-style-span" style="font-size: small; font-weight: normal;">. The Google Places API is an API that returns information about Places. It is part of the Google Maps API Web Services, a collection of HTTP interfaces to Google services providing geographic data for your maps applications. The goal of this article is to go over the API by using </span><span class="Apple-style-span" style="font-size: small; font-weight: normal;"><a href="https://github.com/ddewaele/GooglePlacesApi" target="_blank">a sample application</a></span><span class="Apple-style-span" style="font-size: small; font-weight: normal;"> that actually does these API calls. We'll cover each API request in detail, look at the raw JSON response coming back from the Google Places API, see how to process the response using the </span><span class="Apple-style-span" style="font-size: small; font-weight: normal;"><a href="http://code.google.com/p/google-api-java-client" target="_blank">Google APIs Client Library for Java</a></span><span class="Apple-style-span" style="font-size: small; font-weight: normal;">. We'll also convert the JSON response into a java based model, and use that model to display the results.</span></h2><br />
<a name='more'></a><br />
<br />
<br />
The API covers :<br />
<ul><li>establishments</li>
<li>geographic locations</li>
<li>prominent points of interest</li>
</ul>As most Google APIs, interaction is done using HTTP requests. Applications wishing to access the API will need to have an API key (see google console).<br />
<br />
<h2>Authentication and authorization</h2>An important thing to note is that the Google Places API doesn't require any user authentication. Typically, when interacting with Google APIs, the end-user needs to authenticate in order to authorize access. <br />
For example, in order to use the Google Buzz API to post messages, users needs to authorize the application to post messages on their behalf. <br />
Google supports a number of different ways of performing authentication / authorization,<br />
depending on the API that is being used. <br />
<br />
The Google Places API is a different story. Although you also need to authenticate before being able to use the API, the authentication is done on an application level, based on an API key. After the developer uses the Google API Console to register a project (application), he can retrieve an API key that he needs to use when interacting with the Places API.<br />
<br />
As such, there is no direct link between the user and the Places API. The application is responsible for making the call, and as far as the user is concerned, all calls are anonynomous as he doesn't need to provide a password, or authorize any kind of access. <br />
<br />
<h2>Google Places API Requests</h2>The Google Places API supports 4 basic Place requests :<br />
<ul><li><b>Place Searches</b> : <br />
return a list of nearby Places based on a user's location.</li>
<li><b>Place Details</b> : <br />
requests return more detailed information about a specific Place.</li>
<li><b>Place Check-ins</b> : <br />
allow the application to report that a user has checked in to a Place.</li>
<li><b>Place Reports</b> : <br />
allows the application to add / delete Places to the Place service</li>
</ul><br />
<b>Note : </b>Check-ins have an effect on the place's ranking in your application's Place Search results, and should not be confused with Google Latitude check-ins.<br />
<br />
<h2>Google Places Autocomplete API</h2>An additional API, called the <a href="http://code.google.com/intl/nl/apis/maps/documentation/places/autocomplete.html" target="_blank">Google Places Autocomplete API</a>, can be considered an add-on to the Google Places API. Its a web service that returns Place information based on text search terms, and, optionally, geographic bounds within which to search<br />
<br />
We'll cover the Places Searches / Places Details and the Autocomplete API in this article. <br />
<br />
<h2>Prerequisites</h2>The first thing you'll need to do before interacting with the API is to get yourself an API key. This is done by going to the <a href="https://code.google.com/apis/console" target="_blank">Google API console</a> and retrieving your <b>API Key</b>. Each Google account can login the console, and define different projects (applications). Each project / application gets its own API Key.<br />
<br />
The API key is a required field when interacting with the Google Places API, and needs to be sent with each request. It's used to identify your application,<br />
required to maintain rankings, performing checkins on an application level.<br />
<br />
The next thing you'll need is to get familiar with the different API calls supported by the google places API.<br />
<br />
<h2>Google Places API - Places Search</h2>Searching for places is done through the <strong>https://maps.googleapis.com/maps/api/place/search</strong> endpoint.<br />
<br />
We need to provide the following parameters:<br />
<br />
<table border="1" cellpadding="5"><tbody>
<tr><td>Name</td><td>Description</td></tr>
<tr><td>location</td><td>The latitude/longitude around which to retrieve Place information. This must be provided as a google.maps.LatLng object. This means that we provide a latitude and a longitude value, seperated by a comma (ex: -31.203405,125.244141) </td></tr>
<tr><td>radius</td><td>The distance (in meters) within which to return Place results. The recommended best practice is to set radius based on the accuracy of the location signal as given by the location sensor. Note that setting a radius biases results to the indicated area, but may not fully restrict results to the specified area.</td></tr>
<tr><td>sensor</td><td>ndicates whether or not the Place request came from a device using a location sensor (e.g. a GPS) to determine the location sent in this request. This value must be either true or false.</td></tr>
</tbody></table><br />
The code to execute the search looks like this:<br />
<br />
<pre class="brush: java; toolbar: false;">private static final String PLACES_SEARCH_URL = "https://maps.googleapis.com/maps/api/place/search/json?";
private static final boolean PRINT_AS_STRING = false;
public void performSearch() throws Exception {
try {
System.out.println("Perform Search ....");
System.out.println("-------------------");
HttpRequestFactory httpRequestFactory = createRequestFactory(transport);
HttpRequest request = httpRequestFactory.buildGetRequest(new GenericUrl(PLACES_SEARCH_URL));
request.url.put("key", API_KEY);
request.url.put("location", latitude + "," + longitude);
request.url.put("radius", 500);
request.url.put("sensor", "false");
if (PRINT_AS_STRING) {
System.out.println(request.execute().parseAsString());
} else {
PlacesList places = request.execute().parseAs(PlacesList.class);
System.out.println("STATUS = " + places.status);
for (Place place : places.results) {
System.out.println(place);
}
}
} catch (HttpResponseException e) {
System.err.println(e.response.parseAsString());
throw e;
}
}
</pre><br />
The API responds with the following JSON string that gets converted into a java based model by the <a href="http://code.google.com/p/google-api-java-client" target="_blank">Google APIs Client Library for Java</a>. As you can see it contains a lot of information related to a place, including a reference (that I replaced here to make the code sample more clear). The reference included in this response will be used to retrieve the place details (see later on).<br />
<br />
<pre>{
"status": "OK",
"results": [ {
"name": "Downtown",
"vicinity": "San Francisco",
"types": [ "neighborhood", "political" ],
"geometry": {
"location": {
"lat": 37.7869343,
"lng": -122.4099154
},
"viewport": {
"southwest": {
"lat": 37.7781157,
"lng": -122.4259228
},
"northeast": {
"lat": 37.7957519,
"lng": -122.3939080
}
}
},
"icon": "http://maps.gstatic.com/mapfiles/place_api/icons/geocode-71.png",
"reference": "someRef",
"id": "b7fa5ebba26a10513d18e1fd50628f082ffbd1a2"
}, {
"name": "San Francisco Marriott Marquis",
"vicinity": "Fourth Street, San Francisco",
"types": [ "lodging", "establishment" ],
"geometry": {
"location": {
"lat": 37.7836320,
"lng": -122.3998260
}
},
"icon": "http://maps.gstatic.com/mapfiles/place_api/icons/lodging-71.png",
"reference": "someRef",
"id": "f007d2f91c0070ebdfec60275baa33623f0b771b"
}, {
"name": "Yerba Buena Center for the Arts (YBCA)",
"vicinity": "Mission Street, San Francisco",
"types": [ "establishment" ],
"geometry": {
"location": {
"lat": 37.7861120,
"lng": -122.4020270
}
},
"icon": "http://maps.gstatic.com/mapfiles/place_api/icons/generic_business-71.png",
"reference": "someRef",
"id": "ed6f8fefe44e17ead83bf08f5eb07f8dfe99db93"
} ],
"html_attributions": [ ]
}
</pre>The sample application will output the following, based on our java based model :<br />
<pre>Perform Search ....
-------------------
STATUS = OK
Downtown - b7fa5ebba26a10513d18e1fd50628f082ffbd1a2 - CnRpAAAAeeH91oALhbaI6kgfTP9nfwfFGMhn2NSE_qZQXgk-xC1SimPdw3IevDHlykZ4RKT0O75Gpq2pVnWerUaj9eM2MLPfGhp7scVt78Whl95o_HUTczWggtu-CuwNPNmZTBxgX4o56J-zb4iOLX5A16d00BIQQ8Vf5NYhxzIr3HXIX_DZeRoU1sqwOFEFS5PVKze64-cZbIYosm4
San Francisco Marriott Marquis - f007d2f91c0070ebdfec60275baa33623f0b771b - CoQBdgAAAGg0ekN4sk4cwwipeMUOycf1sJunhbR92OMnZSFY5BqbRyJLwsPtKeCU8pTe-ZLC91h6ldnkAiOTJlioBk7kS-UwDkVvDi5Iq8OjGXiX4m3xL5nmw73S04L9gFmZBTDfhRzVY6pAM337S0XF6WEPqgzPLf71cW3N1Qsjr0a9U-A8EhCfVuJuZJQbdmzGFLtzu5K2GhTVrea_ZS0mdM3QDnPkUAsgU8ry4g
Yerba Buena Center for the Arts (YBCA) - ed6f8fefe44e17ead83bf08f5eb07f8dfe99db93 - CoQBfQAAADtDN0tqr5s5VC6P096Dg7UFjBEI9l8HIOYwQTfV_0yX9G9pyP6OHu8gNOmE_cm-wmipdV1mpuxh4wyYUh0jT2Etbwi-3GjCIAs6d0bjMDrJ84c2Lumy6k17yMn7CcITUGCfIzAyMBZRIIV41PxvFT74DfLJiBw2N3JS6gYVg6jvEhC3CUEuSONQ9G9lf1u-IV2jGhSXvuMFNV9vwzZZPkMhevbUzknkQw
Contemporary Jewish Museum - 350068fffb5f1ac6ffc28a7a54e503f2c578a881 - CoQBcgAAAI20uthq2U3BV576TrBL6iuUJTxsiuLD4UhmJ_VZYB34nMAey2K8W9G_Kkv3EimU_01U3O3blFO2iqRsZGNKf_3tDTYp3dZzkLTcuxvA3FB7v7MlYF8c31UjeRkETTBqlI20XbLqfyHivaIg1XNxMli9n7wVfHn6R7VtVmTXjyRYEhBDc5y7UkwcLh30W7tUVHESGhRf0CHLzW2Zc3fQgb3NaqXBdsMv8A
The W San Francisco Hotel - 2bee7ad63a1626e7193c338e6cce68b3c362236a - CnRwAAAAf4JVWflXis5mdzCgL7S98dIbvZmhrUxfM3o-hyLFZ-yZ4atKUyEPTdxIH6TaTZJE1aHv7nBfGPrvA4Tts7mOkd1aZX-jF1QbFsbracKYFsH51tTeQP9rNFJ2QVrhXypmk2uMWDmbt1n0te302P1HDRIQseiXd5zdOpMFWPaz9TWm_xoUP4mzmfNZTRTIb2oTNFr0sFlwuuc
InterContinental Hotel - fbbcc1bcbf1916a9743f8f85cb98e561ae75c701 - CnRuAAAAE2NiBZYsVcFtSmW9BpplomEXbHUl5aiKlKlZGPRKWvhrUW2ReejnL2Hvq2SEj8JoFOg2znwYbbc9WVeomYDWPfFPNDONy9HDzjBg7yk-wRE97ggSi4-nUwdvgGW5knuhcLq7tmGl5wNlTpaKkQYTlhIQ4b_l0hju6fDzp2r4Gt1ShxoUL75dky-4Yo0JJxyUUEc4_kbMddQ
Moscone Center - abbda52af18357b0575838ff00c7dd6ed69ed866 - CnRmAAAAWh19fWBAb6xH2JcJqvndlIm_4YmKCrDoIfm7He0q8ONzs6Ku-RIDQUTGRm8VdrcV6sHUGYK5LTq6GfHpwFbgGzMJvDVP5Xu-qhcQN27cUf7emjBiuZD0kF6CLRag1m6QDZyHwjGNjOTL-CvyBVbj3BIQH35mtZMTkBwecCQaSPDPERoUJW7Nqo7EmmLikLA_7IL7Pf838ZQ
</pre><br />
Where the <b>request.execute().parseAsString()</b> returns the response coming from the API as raw text, you can see that in this example I've used the <b>request.execute().parseAs(PlacesList.class)</b> to have the response converted into a java based model that's easier to work with. It takes away the plumbing of having to parse the JSON string, and converting it into a model ourselves. It's one of the many interesting features of the <a href="http://code.google.com/p/google-api-java-client" target="_blank">Google APIs Client Library for Java</a>. You simply provide a reference to a java class that has certain fields annotated with the @Key attribute. The client library will simply convert the JSON keys into the java model, by mapping those JSON keys with the fields in your class.<br />
<br />
A simple representation of a list of places looks like this :<br />
<br />
<pre class="brush: java; toolbar: false;">package com.ecs.googleplaces.sample;
import java.util.List;
import com.google.api.client.util.Key;
public class PlacesList {
@Key
public String status;
@Key
public List<place> results;
}
</place></pre><br />
The actual Place object looks like this :<br />
<pre class="brush: java; toolbar: false;">package com.ecs.googleplaces.sample;
import com.google.api.client.util.Key;
public class Place {
@Key
public String id;
@Key
public String name;
@Key
public String reference;
@Override
public String toString() {
return name + " - " + id + " - " + reference;
}
}
</pre>I highly recommend you looking into the <a href="http://code.google.com/p/google-api-java-client" target="_blank">Google APIs Client Library for Java</a>, as it's the preferred way of accessing Google APIs on a variety of platforms (including Android). In fact, it also allows you to call pretty much any REST based API.<br />
<br />
Another thing I would like to highlight here is the <b>HttpRequestFactory httpRequestFactory = createRequestFactory(transport);</b> method. Also part of the <a href="http://code.google.com/p/google-api-java-client" target="_blank">Google APIs Client Library for Java</a>, it offers us a factory to create HTTP GET or POST requests. The method is implemented like this :<br />
<br />
<br />
<pre class="brush: java; toolbar: false;">private static final HttpTransport transport = new ApacheHttpTransport();
public static HttpRequestFactory createRequestFactory(final HttpTransport transport) {
return transport.createRequestFactory(new HttpRequestInitializer() {
public void initialize(HttpRequest request) {
GoogleHeaders headers = new GoogleHeaders();
headers.setApplicationName("Google-Places-DemoApp");
request.headers=headers;
JsonHttpParser parser = new JsonHttpParser();
parser.jsonFactory = new JacksonFactory();
request.addParser(parser);
}
});
}
</pre><br />
As you can seem it sets up a factory based on an HTTP transport. It sets an applicationName on the HTTP headers, and attaches a JSON parser to the HttpRequest. When using Google APIs that cannot be accessed unauthorized, here is where you would setup security mechanisms like OAuth or ClientLogin. As you don't need to be authenticated to use the Places API, the transport doesn't need to be secured. <br />
<br />
<h2>Google Places API - Places Detail</h2>Details of a certain place can also be fetched. For this API call we'll be using the <b>https://maps.googleapis.com/maps/api/place/details</b> endpoint. In order to retrieve the details of a place, we need to provide the following parameters :<br />
<br />
<table border="1" cellpadding="5"><tbody>
<tr><td>Name</td><td>Description</td></tr>
<tr><td>reference</td><td>A textual identifier that uniquely identifies a place, returned from a Place search request.</td></tr>
<tr><td>sensor</td><td>ndicates whether or not the Place request came from a device using a location sensor (e.g. a GPS) to determine the location sent in this request. This value must be either true or false.</td></tr>
</tbody></table><br />
The actual code to perform the API call looks like this :<br />
<br />
<pre class="brush: java; toolbar: false;">public void performDetails(String reference) throws Exception {
try {
System.out.println("Perform Place Detail....");
System.out.println("-------------------");
HttpRequestFactory httpRequestFactory = createRequestFactory(transport);
HttpRequest request = httpRequestFactory.buildGetRequest(new GenericUrl(PLACES_DETAILS_URL));
request.url.put("key", API_KEY);
request.url.put("reference", reference);
request.url.put("sensor", "false");
if (PRINT_AS_STRING) {
System.out.println(request.execute().parseAsString());
} else {
PlaceDetail place = request.execute().parseAs(PlaceDetail.class);
System.out.println(place);
}
} catch (HttpResponseException e) {
System.err.println(e.response.parseAsString());
throw e;
}
}
</pre><br />
<pre>{
"status": "OK",
"result": {
"name": "Moscone Center",
"vicinity": "Howard St, San Francisco",
"types": [ "local_government_office", "establishment" ],
"formatted_phone_number": "(415) 974-4000",
"formatted_address": "747 Howard St, San Francisco, California 94103, United States",
"address_components": [ {
"long_name": "747",
"short_name": "747",
"types": [ "street_number" ]
}, {
"long_name": "Howard St",
"short_name": "Howard St",
"types": [ "route" ]
}, {
"long_name": "San Francisco",
"short_name": "San Francisco",
"types": [ "locality", "political" ]
}, {
"long_name": "San Francisco",
"short_name": "San Francisco",
"types": [ "administrative_area_level_2", "political" ]
}, {
"long_name": "California",
"short_name": "California",
"types": [ "administrative_area_level_1", "political" ]
}, {
"long_name": "94103",
"short_name": "94103",
"types": [ "postal_code" ]
} ],
"geometry": {
"location": {
"lat": 37.7831340,
"lng": -122.4030530
}
},
"rating": 3.7,
"url": "http://maps.google.com/maps/place?cid=13048026345962796583",
"icon": "http://maps.gstatic.com/mapfiles/place_api/icons/generic_business-71.png",
"reference": "CmRdAAAAXjPjnE6U3f7AhWjsg4oesR5iqD8pvugutrRgNZ5kvj_vg3W-9vsf7Br_YEbYY0LYyB7QezEJtJeRvZwo59Te-D_8i5ndaL87oF3aoQBrNWnKQLxmLAQoJF52BuIusy0mEhCzaRLFA1Tbxw3W36FXZWfJGhTvF5dmssLWmDH4gYFJ56tNfwXCog",
"id": "abbda52af18357b0575838ff00c7dd6ed69ed866"
},
"html_attributions": [ ]
}
</pre>The sample application will output the following, based on our java based model :<br />
<pre>Perform Place Detail....
-------------------
Moscone Center - abbda52af18357b0575838ff00c7dd6ed69ed866 - CmRdAAAAPhQ4jEkrlFP3lMq8aixlj04pyv1iHWh055hezINwWUTjj4Z57SmnsBWs3qgwEoUjTBzIyUCwFWxIcgy5R0FyoeC_KrsJyaUO-ryPjkS2BDk4PzDBvtFy_dVJJxmWg038EhDxsgeS0t_7pmAfF4AErbV-GhSJDvgZsZ0pFcce9YtsnFewGsIk8A
</pre><br />
Our java based object model looks like this :<br />
<br />
<pre class="brush: java; toolbar: false;">package com.ecs.googleplaces.sample;
import com.google.api.client.util.Key;
public class PlaceDetail {
@Key
public Place result;
@Override
public String toString() {
if (result!=null) {
return result.toString();
}
return super.toString();
}
}
</pre><br />
<h2>Perform Autocomplete</h2>The autocomplete API allows us to search for places by specifying a search query. It will "predict" places search results based on our (perhaps) incomplete search query.<br />
<br />
The actual code to perform an autocomplete is done through the <strong>https://maps.googleapis.com/maps/api/place/autocomplete</strong> endpoint.<br />
<br />
We provide the following parameters :<br />
<br />
<table border="1" cellpadding="5"><tbody>
<tr><td>Name</td><td>Description</td></tr>
<tr><td>key</td><td>API Key.</td></tr>
<tr><td>input</td><td>The text string on which to search. The Place service will return candidate matches based on this string and order results based on their perceived relevance.</td></tr>
<tr><td>location</td><td>The latitude/longitude around which to retrieve Place information. This must be provided as a google.maps.LatLng object.</td></tr>
<tr><td>radius</td><td>The distance (in meters) within which to return Place results. The recommended best practice is to set radius based on the accuracy of the location signal as given by the location sensor. Note that setting a radius biases results to the indicated area, but may not fully restrict results to the specified area.</td></tr>
<tr><td>sensor</td><td>Indicates whether or not the Place Details request came from a device using a location sensor (e.g. a GPS). This value must be either true or false.</td></tr>
</tbody></table><br />
<pre class="brush: java; toolbar: false;">private static final String PLACES_AUTOCOMPLETE_URL = "https://maps.googleapis.com/maps/api/place/autocomplete/json?";
public void performAutoComplete() throws Exception {
try {
System.out.println("Perform Autocomplete ....");
System.out.println("-------------------------");
HttpRequestFactory httpRequestFactory = createRequestFactory(transport);
HttpRequest request = httpRequestFactory.buildGetRequest(new GenericUrl(PLACES_AUTOCOMPLETE_URL));
request.url.put("key", API_KEY);
request.url.put("input", "mos");
request.url.put("location", latitude + "," + longitude);
request.url.put("radius", 500);
request.url.put("sensor", "false");
PlacesAutocompleteList places = request.execute().parseAs(PlacesAutocompleteList.class);
if (PRINT_AS_STRING) {
System.out.println(request.execute().parseAsString());
} else {
for (PlaceAutoComplete place : places.predictions) {
System.out.println(place);
}
}
} catch (HttpResponseException e) {
System.err.println(e.response.parseAsString());
throw e;
}
}
</pre><br />
A sample response for all places resembling <strong>"mos"</strong> in San Fransisco looks like this (I've replaced the reference strings to make it more clear) :<br />
<pre>{
"status": "OK",
"predictions": [ {
"description": "Moscone Center, Howard Street, San Francisco, CA, United States",
"types": [ "establishment" ],
"reference": "someRef",
"id": "abbda52af18357b0575838ff00c7dd6ed69ed866",
"terms": [ {
"value": "Moscone Center",
"offset": 0
}, {
"value": "Howard Street",
"offset": 16
}, {
"value": "San Francisco",
"offset": 31
}, {
"value": "CA",
"offset": 46
}, {
"value": "United States",
"offset": 50
} ],
"matched_substrings": [ {
"offset": 0,
"length": 3
} ]
}, {
"description": "The Moscone Center, San Francisco, CA, United States",
"types": [ "establishment" ],
"reference": "someRef",
"id": "3256e3f5f9e61141c3a6a9d423db72cbcc52a20d",
"terms": [ {
"value": "The Moscone Center",
"offset": 0
}, {
"value": "San Francisco",
"offset": 20
}, {
"value": "CA",
"offset": 35
}, {
"value": "United States",
"offset": 39
} ],
"matched_substrings": [ {
"offset": 4,
"length": 3
} ]
}, {
"description": "Mosser Co, 4th Street, San Francisco, CA, United States",
"types": [ "establishment" ],
"reference": "someRef",
"id": "f5bf41ca59341fa02820bfa372a30eace9554e6e",
"terms": [ {
"value": "Mosser Co",
"offset": 0
}, {
"value": "4th Street",
"offset": 11
}, {
"value": "San Francisco",
"offset": 23
}, {
"value": "CA",
"offset": 38
}, {
"value": "United States",
"offset": 42
} ],
"matched_substrings": [ {
"offset": 0,
"length": 3
} ]
}, {
"description": "Moss Street, San Francisco, CA, United States",
"types": [ "route", "geocode" ],
"reference": "someRef",
"id": "be49900f2870dc333d762b05acb2d3fb306da7c6",
"terms": [ {
"value": "Moss Street",
"offset": 0
}, {
"value": "San Francisco",
"offset": 13
}, {
"value": "CA",
"offset": 28
}, {
"value": "United States",
"offset": 32
} ],
"matched_substrings": [ {
"offset": 0,
"length": 3
} ]
}, {
"description": "Moscone Center Garage, 3rd Street, San Francisco, CA, United States",
"types": [ "establishment" ],
"reference": "someRef",
"terms": [ {
"value": "Moscone Center Garage",
"offset": 0
}, {
"value": "3rd Street",
"offset": 23
}, {
"value": "San Francisco",
"offset": 35
}, {
"value": "CA",
"offset": 50
}, {
"value": "United States",
"offset": 54
} ],
"matched_substrings": [ {
"offset": 0,
"length": 3
} ]
} ]
}
</pre><br />
<br />
The code outputs the following :<br />
<br />
<pre>Perform Autocomplete ....
-------------------------
Moscone Center, Howard Street, San Francisco, CA, United States - abbda52af18357b0575838ff00c7dd6ed69ed866 - CnRtAAAAShblGHcmcJigrSznSRTAbwMF9JdgmkD_0lxxXV78pMRndcO8fCgmNWdfq80jihJASCAV4jU2HzTmLkXXJvvDZi886vkgXWU5CvRy64Nt3-212qUNrR_a_-3rW11vIODVbjge-gSdkgoRDlYGHB1iIxIQuQQuI76fmECX0Nl0r-FDZxoUiB-QygoIJi-aUDk80XvYsgUOgYk
The Moscone Center, San Francisco, CA, United States - 3256e3f5f9e61141c3a6a9d423db72cbcc52a20d - CnRiAAAA9ZN99q5GQ-rZKRSf_3c9DVxJp0EGwZyY5Fad0W1RAFnBS3BfUxBHrOqNTpZ9LLx-Vl8erDFYRSn-eEGHjHFgTcgjGJs6ojVkkmeCR-AtpOepGFOVoaWXo2-UfOI1JJrFxPZgL3RnYQzKhEbRK4KDUhIQDtbLD5M_jtZTsabOrm0vShoUDbaJ6-WxjVagtZ8fiLmwr_ZpfqQ
Mosser Co, 4th Street, San Francisco, CA, United States - f5bf41ca59341fa02820bfa372a30eace9554e6e - CnRlAAAA48pjj_vpFfZwIM0bUakPgCIU9EsdrjMtWLPMjbw39lapiz6DN6qlKBMP_PQQKqP-5ju6JU8r1Qnw8M-ddH5ZW2v78sFyqCPzpiP6OMRlhuOBOgmsaMxOxqbkZoMvb5EKBe2jM2RHSb69CtChDrKBGxIQMqVs3Mn2uZ2wDzp-KqgvYxoUPX_hvkzXNZ8lAqp9BTgAJjYlCpE
Moss Street, San Francisco, CA, United States - be49900f2870dc333d762b05acb2d3fb306da7c6 - CmRbAAAAHA39Lo-1I8KJhKFYIETdqtu9byFGvzdItkrosiR99clok2OAw0ZrnRiaHwXirkF4Cths3jfsWNF6KJelbWHvp9GJKMZBDD8uVsGVmfStMtBjtW173jHKzRKQPlnyNoR-EhDWP8G0v4ZrLUXrUEgF8EmaGhSaPajMDDjfBPt0Ej3pCg0d1RHEkA
Moscone Center Garage, 3rd Street, San Francisco, CA, United States - f2d897f9f68a31861b864c4035ab0eca1db933c9 - CoQBcQAAADPHf6P13LwU4_8FrAaRNr9qlCZNglkPE1lR70pSuTVYqPwr02MDGgMKauD4A5Q3Tqx8tQjxM546RBu3CWPabS7gfG9zl_n8WI_b5jKn7HMjQA2XPShS3YvuvtblidVvB5UNcLNbdUoulyR7zeXrpiVowsT_1LfXlumTv45UDy_CEhC3ZQ1AZ2Scm-9Mr9S_NCoVGhRm5nitiMq83B7Ng619at7iu-nPHQ
</pre><br />
We've convered the JSON response into a Java based model using the <strong>PlacesAutocompleteList</strong> class.<br />
<br />
<pre class="brush: java; toolbar: false;">package com.ecs.googleplaces.sample;
import java.util.List;
import com.google.api.client.util.Key;
public class PlacesAutocompleteList {
@Key
public List<PlaceAutoComplete> predictions;
public static class PlaceAutoComplete {
@Key
public String id;
@Key
public String description;
@Key
public String reference;
@Override
public String toString() {
return description + " - " + id + " - " + reference;
}
}
}
</pre><br />
<h2>Other API calls supported by the API</h2><br />
<h3>Place Check-Ins</h3>Once you have a reference from a Place Search request, you can use it to indicate that a user has checked in to that Place. Check-in activity from your application is reflected in the Place search results that are returned - popular establishments are ranked more highly, making it easy for your users to find likely matches.<br />
<br />
<b>Note:</b> this is not related to the Google Maps / Latitude API check-ins concept. It's used for an internal ranking within you application.<br />
<br />
<h3>Place Report requests</h3>Place Report requests are used to add new Places, or delete existing ones.<br />
<h2>Conclusions</h2>It's been a long wait, but we can finally start coding against the Places API. Location aware applications can now leverage the Places API to provide "check-in" or other places related activities in their applications. Using the <a href="http://code.google.com/p/google-api-java-client" target="_blank">Google APIs Client Library for Java</a>, getting Google Places up and running in a custom Android application also becomes a breeze.<br />
<h2>References</h2><ul><li><a href="http://code.google.com/intl/nl/apis/maps/documentation/places/" target="_blank">Google Places API</a></li>
<li><a href="http://code.google.com/intl/nl/apis/maps/documentation/places/autocomplete.html" target="_blank">Google Places Autocomplete API</a></li>
<li><a href="https://code.google.com/apis/console" target="_blank">Google API console</a></li>
<li><a href="http://code.google.com/p/google-api-java-client" target="_blank">Google APIs Client Library for Java</a></li>
<li><a href="http://json.org/" target="_blank">Json</a></li>
<li><a href="https://github.com/ddewaele/GooglePlacesApi" target="_blank">Google Places API Sample</a></li>
</ul></div>ddewaelehttp://www.blogger.com/profile/00193914878294828713noreply@blogger.com90tag:blogger.com,1999:blog-1924751098502050632.post-5917886997682390902011-05-16T14:37:00.000-07:002011-05-16T14:50:21.688-07:00Introducing the Google Fusion Tables API<div dir="ltr" style="text-align: left;" trbidi="on"><table border="0"><tbody>
<tr><td><br />
In this article, I'll be providing you with a quick overview of the Fusion Tables API.<br />
<a href="http://www.google.com/fusiontables/public/tour/index.html" target="_blank">Google Fusion Tables</a> is a modern data management and publishing web application that makes it easy to host, manage, collaborate on, visualize, and publish data tables online. We'll go over the API by creating a sample application that interacts with our Fusion Tables. <span class="Apple-style-span" style="-webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px;">The sample app will use the </span><span class="Apple-style-span" style="-webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px;"><a href="http://code.google.com/p/google-api-java-client/" target="_blank">Google APIs Client Library for Java</a> and the complete source code is available on GitHub.</span></td><td><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaqtYNngnzSyvgU3T3kEFAbCBIP-b3n8G5dKRxyH_9oiMLB2chbqiVYwyzZPEg7lb5j4Uvoy5oAoAgcOi9hVMf0IGBKPbIeQz5JbMDMDCx7HBDu7BQl9jlQojF_qQ-wiPejsSEoSv1TML-/s1600/fusion_tables-128_reasonably_small.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaqtYNngnzSyvgU3T3kEFAbCBIP-b3n8G5dKRxyH_9oiMLB2chbqiVYwyzZPEg7lb5j4Uvoy5oAoAgcOi9hVMf0IGBKPbIeQz5JbMDMDCx7HBDu7BQl9jlQojF_qQ-wiPejsSEoSv1TML-/s1600/fusion_tables-128_reasonably_small.jpg" /></a></div></td></tr>
</tbody></table><br />
<br />
<a name='more'></a><br />
<br />
The Google Fusion Tables has <a href="http://www.google.com/fusiontables" target="_blank">a web frontend</a>, where you can create, view and share Fusion Tables.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRuoV7ktobUqRLDiVWZYTA9dxHWxqve046XtqVaO7ZYQO18FgDOSTQBsZFXcyW77gn0lwg_MfSAaDj5Tsj_GVghLFklzqQJpRp5t1GmqFxsC8F_iMoClVo8xKSfodY7sunQ2UUMgg7qKib/s1600/FusionTableAllTables.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="293" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRuoV7ktobUqRLDiVWZYTA9dxHWxqve046XtqVaO7ZYQO18FgDOSTQBsZFXcyW77gn0lwg_MfSAaDj5Tsj_GVghLFklzqQJpRp5t1GmqFxsC8F_iMoClVo8xKSfodY7sunQ2UUMgg7qKib/s400/FusionTableAllTables.PNG" width="400" /></a></div><br />
<br />
Fustion Tables also comes with a <a href="http://code.google.com/apis/fusiontables/" target="_blank">Fusion Tables API</a> that allows developers to interact with the Fusion Tables using different programming languages.<br />
<br />
We'll go over the Fusion Tables API by creating a sample app (the complete source code for the app is available in the <a href="https://github.com/ddewaele/FusionTablesJ2SE">FusionTablesJ2SE GitHub repository</a>.)<br />
The sample app will use the <a href="http://code.google.com/p/google-api-java-client/" target="_blank">Google APIs Client Library for Java</a>. This is a java client library to access a variety of different Google APIs. Although the API doesn't officially support Google Fusion Tables, it will allow us to <br />
<ul><li>Setup the authenticated transport.</li>
<li>Make actual calls to the Fusion Tables API.</li>
<li>Allow us to plugin a custom CSV Parser for transforming the APIs responses</li>
</ul><h2>The Fusion Tables API</h2>According to the Fusion Tables API :<br />
<br />
"The Google Fusion Tables API is a set of statements that you can use to search for and retrieve Google Fusion Tables data, insert new data, update existing data, and delete data. The API statements are sent to the Google Fusion Tables server using HTTP GET requests (for queries) and POST requests (for inserts, updates, and deletes) from a Web client application."<br />
<br />
So we're basically dealing with a REST API (like most Google APIs). We'll use the Google APIs Client Library for Java to interact with Fusion Tables.<br />
<br />
The API allows us to :<br />
<ul><li>Exploring tables</li>
<li>Creating a table</li>
<li>Creating a view</li>
<li>Querying for data</li>
<li>Adding rows</li>
<li>Updating a row</li>
<li>Deleting rows</li>
<li>Deleting a table</li>
<li>Working With Geographic Data</li>
</ul>We'll be executing all these calls from our sample application.<br />
<br />
All API calls return a response in a CSV format. This means that when selecting records from a Fusion Table, the response will look like this:<br />
<pre>description,name,accuracy,timestamp,geometry
the description,the name,30,1305362833553,"<point><coordinates>3.517819,50.962329,0.0</coordinates></point>"
the description2,the name2,50,1305362833553,"<point><coordinates>3.517819,50.962329,0.0</coordinates></point>"
</pre>Working with CSV in a java environment can be cumbersome,as it doesn't really map well to an object oriented structure. Most Google APIs return responses in either the JSON/Atom format, that can be easily mapped to a java object model.<br />
<br />
At the time of writing, there is no Parser available in the <a href="http://code.google.com/p/google-api-java-client/" target="_blank">Google APIs Client Library for Java</a> that will parse the CSV response into a java based model. The API does however allow us to plugin a custom parser for handling these responses.<br />
<br />
For this example, I've created a <a href="https://github.com/ddewaele/FusionTablesJ2SE/blob/master/src/main/java/com/ecs/fusiontables/sample/CsvParser.java" target="_blank">CsvParser</a> that's included in the project source code. The goal of this parser is to allow you to capture the CSV responses from the Fusion Tables API, and transform that into a java based model. <br />
<br />
<h2>The sample application</h2>The sample JSSE based app interacts with Fusion Tables through the Fusion Tables API. It uses ClientLogin for authentication. (see the authentication section).<br />
<br />
The sample application does the following:<br />
<ul><li>Show the Fusion Tables associated with the user account.</li>
<li>Create a new Fusion Table</li>
<li>Show the Fusion Tables associated with the user account. (new table is added)</li>
<li>Performs a select on the new table (empty)</li>
<li>Insert a record in the new table.</li>
<li>Performs a select on the new table (new record is shown).</li>
<li>Drops the table.</li>
</ul><br />
The sample application will output the following:<br />
<pre>+++ Begin Show Tables
No tables found
+++ Create Table
CVS Response
------------
tableid
852154
Table with ID = 852154 created
+++ Begin Show Tables
CVS Response
------------
table id,name
852154,TEST_TABLE
Found table : TEST_TABLE(852154)
+++ Select from Tables
CVS Response
------------
description,name,accuracy,timestamp,geometry
No records found in table 852154
+++ Insert into Tables
CVS Response
------------
rowid
1
2
record inserted : 1
record inserted : 2
+++ Select from Tables
CVS Response
------------
description,name,accuracy,timestamp,geometry
the description,the name,30,1305580967127,"<point><coordinates>3.517819,50.962329,0.0</coordinates></point>"
the description2,the name2,50,1305580967127,"<point><coordinates>3.517819,50.962329,0.0</coordinates></point>"
Found record : the name
Found record : the name2
+++ Drop Tables
</pre><br />
<h2>Setting up the project</h2>We'll configure the project in Eclipse, and use Maven to define the dependencies. I would suggest installing <a href="http://m2eclipse.sonatype.org/" target="_blank">m2eclipse</a> in order to resolve the dependencies properly, and have your project setup correctly in Eclipse.<br />
<br />
If you don't want to install m2eclipse, you'll need to execute the following command from the project folder :<br />
<pre>mvn eclipse:clean eclipse:eclipse
</pre>The project includes the following pom.xml file, where the com.google.api.client dependencies are defined. <br />
<br />
<pre class="brush: xml; toolbar: false;"><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.google</groupId>
<artifactId>google</artifactId>
<version>5</version>
</parent>
<groupId>com.ecs.fusiontables.j2se</groupId>
<artifactId>fusiontables-j2se</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.google.api.client</groupId>
<artifactId>google-api-client</artifactId>
<version>1.4.1-beta</version>
</dependency>
<dependency>
<groupId>com.google.api.client</groupId>
<artifactId>google-api-client-googleapis</artifactId>
<version>1.3.2-alpha</version>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-embedded</artifactId>
<version>6.1.24</version>
<scope>compile</scope>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
</pre><br />
Your project will look like this:<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-zKRzt-k9dsU3uUfi2pnuVvsKdEAjRR85WtqTFV-GKCh5YUiwpKYFIetbS6rXDAvwfI5BagsOc_ptlZ389CczetLpF_UlujLuZZ83nMQ3F1mt2SXhCEAx14VnDsMuncgTErXsV9Dh0f3q/s1600/EclipseProjectSetup.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="382" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-zKRzt-k9dsU3uUfi2pnuVvsKdEAjRR85WtqTFV-GKCh5YUiwpKYFIetbS6rXDAvwfI5BagsOc_ptlZ389CczetLpF_UlujLuZZ83nMQ3F1mt2SXhCEAx14VnDsMuncgTErXsV9Dh0f3q/s400/EclipseProjectSetup.PNG" width="400" /></a><br />
<br />
The project includes the following dependencies. If you don't want to use Maven to handle the dependencies, you'll need to ensure that the sample has includes the following dependencies:<br />
<ul><li>/commons-codec/commons-codec/1.3/commons-codec-1.3.jar</li>
<li>/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.jar</li>
<li>/com/google/api/client/google-api-client/1.3.2-alpha/google-api-client-1.3.2-alpha.jar</li>
<li>/com/google/api/client/google-api-client-googleapis/1.3.2-alpha/google-api-client-googleapis-1.3.2-alpha.jar</li>
<li>/com/google/code/gson/gson/1.6/gson-1.6.jar</li>
<li>/com/google/guava/guava/r08/guava-r08.jar</li>
<li>/org/apache/httpcomponents/httpclient/4.0.3/httpclient-4.0.3.jar</li>
<li>/org/apache/httpcomponents/httpcore/4.0.1/httpcore-4.0.1.jar</li>
<li>/org/codehaus/jackson/jackson-core-asl/1.6.5/jackson-core-asl-1.6.5.jar</li>
<li>/com/google/code/findbugs/jsr305/1.3.9/jsr305-1.3.9.jar</li>
<li>/xpp3/xpp3/1.1.4c/xpp3-1.1.4c.jar</li>
</ul><br />
<h2>Setting up the transport</h2>As all communication towards the Fusion Tables API is done through HTTP, we'll start by setting up our HTTP transport. As we're running in a J2SE environment, we'll use the ApacheHttpTransport. <br />
<br />
<pre class="brush: java; toolbar: false;">private static final HttpTransport transport = new ApacheHttpTransport();
</pre><br />
We've defined our transport on an abstract FusionTablesCommand base class that will handle the communication with the API. The <a href="http://code.google.com/p/google-api-java-client/" target="_blank">Google APIs Client Library for Java</a> v!.4.1 (that has gone into beta recently) introduces a new way of handling transports that we'll be using in the sample application.<br />
<br />
The Google APIs Client Library for Java introduces a thread-safe light-weight HTTP request factory layer on top of the HTTP transport for initializing requests.<br />
<br />
<pre class="brush: java; toolbar: false;">private static HttpTransport transport = new ApacheHttpTransport();
public static HttpRequestFactory createRequestFactory(
final HttpTransport transport) {
return transport.createRequestFactory(new HttpRequestInitializer() {
public void initialize(HttpRequest request) {
GoogleHeaders headers = new GoogleHeaders();
headers.setApplicationName("Google-FusionTables/1.0");
request.headers=headers;
request.addParser(new CsvParser());
try {
authorizeTransport(request);
} catch (Exception e) {
e.printStackTrace();
}
}
});
</pre><br />
The HttpRequestFactory is exposed on our FusionTablesCommand base class. When our application needs to build an HTTP GET or POST request, it will use this HttpRequestFactory to obtain such a request object. The HttpRequestFactory is configured in such a way that <br />
<br />
<ul><il>Proper headers are being set on the request.
<li>A custom CSV parser is but on the request, needed to convert the HTTP response into a java object</li>
<li>Authorization headers are set on the request for authenticating</li>
</il></ul><br />
<h2>Setting up authentication</h2>On of the first things we need to setup is the authenticated transport over HTTP to the Google Fusion Tables endpoint. Fusion Tables supports 2 authentication methods<br />
<br />
<ul><li>ClientLogin</li>
<li>OAuth</li>
</ul><br />
In this sample, we'll be using ClientLogin. The advantage of ClientLogin is that it's very quick and easy to setup. You basically provide a username/password and your HTTP transport will be authenticated. I wouldn't go so far as saying that this is the method you would like for your production code, however keep in mind that this is a very simple sample running on a J2SE environment, and as we're focussing on the Fusion Tables API, and not so much on the various ways of authenticating against the API, ClientLogin is a good fit here.<br />
<br />
<pre class="brush: java; toolbar: false;">private void authorizeTransport(HttpRequest request) throws HttpResponseException, IOException {
// authenticate with ClientLogin
ClientLogin authenticator = new ClientLogin();
authenticator.authTokenType = Constants.AUTH_TOKEN_TYPE;
authenticator.username = Constants.USERNAME;
authenticator.password = Constants.PASSWORD;
authenticator.transport = transport;
try {
Response response = authenticator.authenticate();
request.headers.authorization=response.getAuthorizationHeaderValue();
} catch (HttpResponseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
</pre><br />
<h2>The FusionTablesCommand</h2>For the sample project, we've create an abstract FusionTablesCommand that will execute the Fusion Tables API requests over HTTP. As you can see, we're using the https://www.google.com/fusiontables/api/query endpoint for all of our API requests.<br />
In order to execute a query against the Fusion Tables API, a concrete command needs to provide a SQL statement to be executed. The abstract FusionTablesCommand takes care of handling the transport and exeucting the request.<br />
<br />
<pre class="brush: java; toolbar: false;">public abstract class FusionTablesCommand {
protected static final String FUSION_TABLES_API_QUERY = "https://www.google.com/fusiontables/api/query";
protected static final String SHOW_TABLES = "SHOW TABLES";
private static final HttpTransport transport = new ApacheHttpTransport();
public static final HttpRequestFactory httpRequestFactory = createRequestFactory(transport);
private String sql;
public FusionTablesCommand(String sql) {
this.sql = sql;
}
protected abstract HttpRequest getHttpRequest() throws Exception;
public DataList execute() throws Exception {
HttpRequest httpRequest = getHttpRequest();
httpRequest.url.put("sql", sql);
return httpRequest.execute().parseAs(DataList.class);
}
</pre><br />
As mentioned in the documentation :<br />
<ul><li>HTTP GET requests are used for executing queries</li>
<li>HTTP POST requests are used for inserts, updates, and deletes</li>
</ul>As such, we've modelled a command to perform the GET requests :<br />
<pre class="brush: java; toolbar: false;">public FusionTablesGetCommand(String sql) {
super(sql);
}
@Override
protected HttpRequest getHttpRequest() throws Exception {
return httpRequestFactory
.buildGetRequest(new GenericUrl(FUSION_TABLES_API_QUERY));
}
</pre><br />
And a command to execute the POST requests :<br />
<pre class="brush: java; toolbar: false;">package com.ecs.fusiontables.sample.command;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpRequest;
public class FusionTablesPostCommand extends FusionTablesCommand {
public FusionTablesPostCommand(String sql) {
super(sql);
}
@Override
protected HttpRequest getHttpRequest() throws Exception {
return httpRequestFactory.buildPostRequest(
new GenericUrl(FUSION_TABLES_API_QUERY), null);
}
}
</pre>The only difference when comparing the POST command to the GET command is that the PostRequest allows you to specify the HTTP request content. We;re not sending any content besides the URL so we're specifying null here.<br />
<br />
Notice how our httpRequestFactory is used here in the 2 concrete classes to create a GET and a POST request. Both are using the standard fusion tables endpoint.<br />
<br />
<h2>Showing Table information</h2>Now that we have the basic infrastructure setup, and the ability to do POST and GET request, we can start by implementing our first Fusion Tables API call.<br />
The first thing we'll do in our sample is display the tables that are assigned to our user account. This is done using the following code :<br />
<br />
<pre class="brush: java; toolbar: false;">FusionTablesCommand showTablesCommand = new FusionTablesGetCommand(transport,SQL_SHOW_TABLES);
TableInfoList tableList = showTablesCommand.execute(TableInfoList.class);
List<TableInfo> records = tableList.records;
if (records.size()==0) {
System.out.println("No tables found.");
} else {
for (TableInfo table : records) {
System.out.println("Found table : " + table.name + "(" + table.table_id + ")");
}
}
</pre><br />
Notice how we're using the FusionTablesGetCommand (HTTP GET Request) and how we're passing on a SQL statement (SHOW TABLES).<br />
<br />
The SQL statement SHOW TABLES returns all tables associated with our account. If this is the first time you're using Fusion Tables, the resultset will be empty.<br />
If you already have tables associated with your account, the response will look like this :<br />
<br />
<pre>table id,name
831290,TEST_TABLE
831291,TEST_TABLE2
</pre><br />
This CSV response is handled by our Custom CSV Parser, and transformed into the java model below:<br />
<br />
<pre class="brush: java; toolbar: false;">package com.ecs.fusiontables.sample.model;
import java.util.List;
import com.google.api.client.util.Key;
public class TableInfoList {
@Key
public List<tableinfo> records;
public static class TableInfo {
@Key(value="table id")
public String table_id;
@Key
public String name;
}
}
</tableinfo></pre>That way, instead of dealing with the CSV string, we now have a clean datamodel to develop against. The code snippet above just loops over the TableInfo records, and prints out the table id and table name.<br />
<br />
<h2>Creating a table</h2><br />
<pre class="brush: java; toolbar: false;">private static final String SQL_CREATE_TABLE = "CREATE TABLE 'TEST_TABLE' (description: STRING,name: STRING,accuracy: NUMBER, timestamp: NUMBER, geometry: LOCATION);";
FusionTablesCommand createTableCommand = new FusionTablesPostCommand(SQL_CREATE_TABLE);
DataList list = createTableCommand.execute();
System.out.println("Table with ID = " + list.getRecords().get(0).tableid + " created");
</pre><br />
The API will respond with the tableId if the table creation was succesfull.<br />
<pre>tableid
831290
</pre><br />
You can see in the Fusion Tables website that the table was created :<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmU0XKT0ArJ76upLfSiVgWThkxmJP4_bDLN37nQb_iACD_wDe3lwNhbsnmw7wiihu36sVVeh2HlsrR6ljZX_loMAycOd2gKoJf8aM1OGj6F7BPqfVsSu75DOWz-mWrrMMor8_FX34oP4EP/s1600/FusionTablesOverview.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="304" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmU0XKT0ArJ76upLfSiVgWThkxmJP4_bDLN37nQb_iACD_wDe3lwNhbsnmw7wiihu36sVVeh2HlsrR6ljZX_loMAycOd2gKoJf8aM1OGj6F7BPqfVsSu75DOWz-mWrrMMor8_FX34oP4EP/s640/FusionTablesOverview.PNG" width="640" /></a><br />
<h2>Selecting data</h2>We'll use the following sode snippet to select data from our table. We pass on a SELECT statement, and use our <b>tableId</b> in the FROM clause. (keep in mind that FusionTables allows you to create different tables with the same name. The only unique key is the talbeId<br />
<br />
<pre class="brush: java; toolbar: false;">public void selectFromTable(String tableId) throws Exception {
System.out.println(" +++ Select from Tables");
FusionTablesCommand getTableCommand = new FusionTablesGetCommand(transport,"SELECT description, name,accuracy,timestamp,geometry FROM "+ tableId);
TableRecords tableRecords = getTableCommand.execute(TableRecords.class);
if (tableRecords.records.size()==0) {
System.out.println("No records found in table " + tableId);
} else {
for (TableRecords.TableRecord tableRecord : tableRecords.records) {
System.out.println("Found record : " + tableRecord.name);
}
}
System.out.println("");
}
</pre><br />
The Fusion Tables API responds to a select by returning the following CSV value (when 2 records are stored in the database). At this point you might be wondering what the geometry column is. The geometry column contains a geographic location (point) identified by a longitude latitude. Fusion Tables allows you to visualize such data on a map. But more on this in a future blog post.<br />
<br />
<pre>description,name,accuracy,timestamp,geometry
the description,the name,30,1305362833553,"<point><coordinates>3.517819,50.962329,0.0</coordinates></point>"
the description2,the name2,50,1305362833553,"<point><coordinates>3.517819,50.962329,0.0</coordinates></point>"
</pre>Our CsvParser will transform this into a java data model. Notice how we execute the command by specifying a TalbeRecords.class.<br />
<br />
<pre>TableRecords tableRecords = getTableCommand.execute(TableRecords.class);
</pre><br />
For this particular table, we've create a simple java POJO like this :<br />
<br />
<pre class="brush: java; toolbar: false;">package com.ecs.fusiontables.sample.model;
import java.util.List;
import com.google.api.client.util.Key;
public class TableRecords {
@Key
public List<tablerecord> records;
public static class TableRecord {
@Key
public String description;
@Key
public String name;
@Key
public String accuracy;
@Key
public String timestamp;
@Key
public String geometry;
}
}
</tablerecord></pre><br />
As you can see it exposes a list of TableRecord objects. Each column in our table is mapped to a field in the TableRecord class through the com.google.api.client.util.Key annotation. The custom CsvParser that we created will ensure that the CSV values (representing the coiumns in the table) get injected properly into this TableRecord.<br />
<br />
<h2>Inserting data</h2>The Fusion Table API allows you to insert data into a table by using a simple INSERT statement. The following code illustrates this:<br />
<pre class="brush: java; toolbar: false;">public void insertIntoTable(String tableId) throws Exception {
System.out.println(" +++ Insert into Tables");
String point = "<point><coordinates>3.517819,50.962329,0.0</coordinates></point>";
String sql1 = "INSERT INTO "
+ tableId
+ " (name,description, accuracy,timestamp,geometry) VALUES ('the name','the description',30,"
+ System.currentTimeMillis() + ",'" + point + "');";
String sql2 = "INSERT INTO "
+ tableId
+ " (name,¨description, accuracy,timestamp,geometry) VALUES ('the name2','the description2',50,"
+ System.currentTimeMillis() + ",'" + point + "');";
FusionTablesCommand insertTableCommand = new FusionTablesPostCommand(transport, sql1 + sql2);
RowList rowList = insertTableCommand.execute(RowList.class);
if (rowList.records.size()==0) {
System.out.println("No records inserted.");
} else {
for (Row row : rowList.records) {
System.out.println("record inserted : " + row.rowid);
}
}
System.out.println("");
}
</pre>When the insert was successful, the Fusion Tables API will respond with the ROWIDs of the records that got inserted. In our example, we added 2 records, so the API returns the following.<br />
<br />
<pre>rowid
1
2
</pre>We've again modeled this response in a java object model that looks like this:<br />
<pre class="brush: java; toolbar: false;">package com.ecs.fusiontables.sample.model;
import java.util.List;
import com.google.api.client.util.Key;
public class RowList {
@Key
public List<Row> records;
public static class Row {
@Key
public String rowid;
}
}
</pre><br />
Again, you can check the Fusion Tables website<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9kgA6yUWoUOmhlLvy7S7zMF46imaCIgNv9Zch_p06wpAJkXieaPWnq_nS_UBsZj4gvJYz_RJdDsQeNHNFrS9E5b_J2xGaEuvqLdeYfFeHCm7NsAuUk1mMSKkk1WswhbBLqVqFpDmKExrN/s1600/FusionTableSelected.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="304" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9kgA6yUWoUOmhlLvy7S7zMF46imaCIgNv9Zch_p06wpAJkXieaPWnq_nS_UBsZj4gvJYz_RJdDsQeNHNFrS9E5b_J2xGaEuvqLdeYfFeHCm7NsAuUk1mMSKkk1WswhbBLqVqFpDmKExrN/s640/FusionTableSelected.PNG" width="640" /></a></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><br />
</div><div class="separator" style="clear: both; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: center;"><br />
</div><br />
<h2>Dropping a table</h2>We use the following code to drop a table.<br />
<pre class="brush: java; toolbar: false;">public void dropTable(String tableId) throws Exception {
System.out.println(" +++ Drop Tables");
FusionTablesCommand dropTableCommand = new FusionTablesPostCommand(transport,"DROP TABLE " + tableId);
dropTableCommand.execute(null);
}
</pre>The API simply returns "OK" when the drop was successful<br />
<pre>OK
</pre>It throws an exception when the drop couldn't take place (in case of an invalid tableId for example)<br />
<h2>Conclusion</h2>In this article, we went over the Fusion Tables API hands on, by writing a java based sample application that uses the Google APIs Client Library for Java. We've made various calls to the API, and used a custom parser to handle the CSV based responses we received from the API.<br />
The API is very easy to use, and provides the basic functionality you would expect when interacting with Fusion Tables. However, some key parts are currently missing to fully integrate it in real world application. The ability to set visibility on tables, share them with specific users is currently not available in the API (although it is being logged in the issue tracker, so hopefully it will become available soon). <br />
<h2>References</h2><ul><li><a href="http://www.google.com/fusiontables/Home" target="_blank">Google Fusion Tables</a></li>
<li><a href="http://code.google.com/apis/fusiontables/" target="_blank">Google Fusion Tables API</a></li>
<li><a href="http://code.google.com/p/google-api-java-client/" target="_blank">Google APIs Client Library for Java</a></li>
<li><a href="https://github.com/ddewaele/FusionTablesJ2SE">FusionTablesJ2SE GitHub repository</a></li>
<li><a href="http://m2eclipse.sonatype.org/" target="_blank">m2eclipse</a></li>
</ul></div>ddewaelehttp://www.blogger.com/profile/00193914878294828713noreply@blogger.com6tag:blogger.com,1999:blog-1924751098502050632.post-87263304749012551312011-05-10T14:03:00.001-07:002011-05-10T14:03:46.564-07:00Google Places API now available for all developers !<div dir="ltr" style="text-align: left;" trbidi="on"><br />
<div dir="ltr" style="text-align: left;" trbidi="on">The <a href="http://code.google.com/apis/maps/documentation/places/" target="_blank">Google Places API</a> has been made available to all developers.<br />
<br />
Where before, the Places API was available through Google invitation only (via the Google Places API Application Form) :<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2VYRVBKGvxrT6aHG5BR406QWO-rgCuRXFSrWiY3pyKwHlN62qERTGXw4C1hwttsnMSQ8OuX5CQm7T_r5j3Qm28Oku8n_IEWNtvK6lPo-CEAlhRUMhjJGjrcpqKrfoS_1uSMLnRBP6bRoc/s1600/places_disabled.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="303" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2VYRVBKGvxrT6aHG5BR406QWO-rgCuRXFSrWiY3pyKwHlN62qERTGXw4C1hwttsnMSQ8OuX5CQm7T_r5j3Qm28Oku8n_IEWNtvK6lPo-CEAlhRUMhjJGjrcpqKrfoS_1uSMLnRBP6bRoc/s400/places_disabled.PNG" width="400" /></a></div><br />
The Places API can now be activated in the Google API Console.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyrbshBD9NugtdzDtPb-qBr_Fq6r8LxghYpjkJqiiSSJMjlqcP7YhMZpDWr38XWkDMX_q4HeEGCHH3xb9Ry1AipdpRNf_lQrO_GkajyxyB3HDqKYeyfwGYYz5e6ru066UoJ4EOju_A98nM/s1600/places_enabled.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="325" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyrbshBD9NugtdzDtPb-qBr_Fq6r8LxghYpjkJqiiSSJMjlqcP7YhMZpDWr38XWkDMX_q4HeEGCHH3xb9Ry1AipdpRNf_lQrO_GkajyxyB3HDqKYeyfwGYYz5e6ru066UoJ4EOju_A98nM/s400/places_enabled.PNG" width="400" /></a></div><br />
Let the coding begin !<br />
<br />
<br />
</div></div>ddewaelehttp://www.blogger.com/profile/00193914878294828713noreply@blogger.com1tag:blogger.com,1999:blog-1924751098502050632.post-81273209725481780252011-05-08T06:43:00.000-07:002011-05-10T00:49:46.644-07:00Having fun at the Google OAuth Playground<h1>Introduction</h1><table border="0"><tbody>
<tr><td><br />
The <a href="http://googlecodesamples.com/oauth_playground/">Google OAuth playground</a> is a great way to test the integration with Google services using the various Google APIs. The OAuth playground focusses on those APIs that use OAuth as an authentication mechanism.<br />
In this post, I'll be showing you how to interact with the Google Buzz API from the OAuth playground.<br />
</td> <td><br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0uPI8aKDNpdYvbB9evn6trcPOEnRFrmZvLsABVj3mnBGkrUEdwMfvBX61FxE5YXuJFoPXINdObjR6mds8cJipt9LPjUZhAL8YBUPtZCg48UbuPxvxeykgo_IQshVrG5KhCQXaYjWF6q0Y/s1600/oauth-playground-logo.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0uPI8aKDNpdYvbB9evn6trcPOEnRFrmZvLsABVj3mnBGkrUEdwMfvBX61FxE5YXuJFoPXINdObjR6mds8cJipt9LPjUZhAL8YBUPtZCg48UbuPxvxeykgo_IQshVrG5KhCQXaYjWF6q0Y/s1600/oauth-playground-logo.PNG" /></a><br />
</td> </tr>
</tbody></table><a name='more'></a><br />
OAuth is not the easiest thing to test on a standalone or isolated environment, as it requires human interaction, popping a browser, and getting an access token from the browser to your application. (something that's rather problematic and difficult for most desktop / mobile applications).<br />
So if you're developing an application, and you want to focus on the actual API (request/response interaction with Google), handing off the OAuth part to the OAuth playground can be a helpfull thing.<br />
The process is really simple, and in 2 minutes time, you'll be able to retrieve your Google Buzz Feed using the API, and post a message to Google Buzz.<br />
<h2>Google OAuth Playground page</h2>OK, let's get things started. First, direct your browser to the <a href="http://googlecodesamples.com/oauth_playground/">Google OAuth Playground</a> page. You'll notice the site is divided into 6 distinct zones:<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNqybvPZ0XG21ETWkSSgS0lJ-0DE9jJYW-VbgqWLied4tiSCtxuSvjbtrMuujr6syPDlmsuODjZa2r5zPbYNScRTnhVnw9oxUA4iZ6qLNLFp10vP8S72jN-Gpd1HTmFEZ-MM94XNzUIyyu/s1600/oauth-playground-full.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="298" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNqybvPZ0XG21ETWkSSgS0lJ-0DE9jJYW-VbgqWLied4tiSCtxuSvjbtrMuujr6syPDlmsuODjZa2r5zPbYNScRTnhVnw9oxUA4iZ6qLNLFp10vP8S72jN-Gpd1HTmFEZ-MM94XNzUIyyu/s400/oauth-playground-full.PNG" width="400" /></a><br />
<br />
<ol style="text-align: left;"><li>Choose your scope</li>
<li>Modify the OAuth Parameters</li>
<li>Get a Request Token</li>
<li>Authorize the Request Token</li>
<li>Upgrade to an Access Token</li>
<li>Use the token</li>
</ol><br />
<h2>Choose your scope</h2>The first thing we'll do is pick a <b>scope </b>for our OAuth interaction. In this particular case, we'll choose the Google Buzz scope.<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLwR670VlURWcTCgIYjPItnNCop9ZpY8Yr-sxtw_O_FS5Doxw60LRf2j7D6Ml_vZ7j6vu9TLxGWH3lSjDS3TErivHFqFL7LgVCtj24D-HNRbM6nC8blGqkuaWDEdd1bP995hLXt9DEIQt3/s1600/1-select-scope.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLwR670VlURWcTCgIYjPItnNCop9ZpY8Yr-sxtw_O_FS5Doxw60LRf2j7D6Ml_vZ7j6vu9TLxGWH3lSjDS3TErivHFqFL7LgVCtj24D-HNRbM6nC8blGqkuaWDEdd1bP995hLXt9DEIQt3/s640/1-select-scope.PNG" /></a><br />
<br />
<br />
There's no need to modify the OAuth Parameters for the Google Buzz scope.<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2PjOEDPm-3RKwjHIpKUXCPUhyphenhyphen_XLwz5pkKe_NQAQWmBznPtDdLE1ZMB-SjcmVIje1o1VLpNi-uNwj_RQ75KQx05AIXC9pDws2MZeeoBuZC8d0Jc2OVQiRWOHAACKRZU0t5VN0Z1enaL9A/s1600/2-modify-oauth-parameters.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="176" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2PjOEDPm-3RKwjHIpKUXCPUhyphenhyphen_XLwz5pkKe_NQAQWmBznPtDdLE1ZMB-SjcmVIje1o1VLpNi-uNwj_RQ75KQx05AIXC9pDws2MZeeoBuZC8d0Jc2OVQiRWOHAACKRZU0t5VN0Z1enaL9A/s640/2-modify-oauth-parameters.PNG" /></a><br />
<br />
<br />
<br />
<h2>Retrieve the Request Token</h2>We start the OAuth flow by clicking on the <b>Request Token</b> button.<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrcM2sDxD4NDX54QFrFVwbXmL8isYW3fSP0Nkwc5Cjtr1A-fWjx14O5b4pFKvwTHDIWZZlhbh9OVynrWynCFIHHmHZmyicU8XamaYSlrH-7Adu6-CxupbhWJVVXHgZky8sAUmbgNCb-3H_/s1600/3-get-request-token.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrcM2sDxD4NDX54QFrFVwbXmL8isYW3fSP0Nkwc5Cjtr1A-fWjx14O5b4pFKvwTHDIWZZlhbh9OVynrWynCFIHHmHZmyicU8XamaYSlrH-7Adu6-CxupbhWJVVXHgZky8sAUmbgNCb-3H_/s640/3-get-request-token.PNG" /></a><br />
<br />
As you can see in zone 2, a <b>Request Token</b> (oauth_token) was provided to us. This was done without user intervention by doing a call to Google. We need this request token for the next steps.<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgplT9i6Z3Yr1n-vyrqg5p6ymFedIOTQ6_mJGvqE-oZeB4eWkedOqPScPYknskGhE6itTJCh7rtXVYYYWzgThxFh9F8lLFt40yemfCP2iZnVTCWiF3qjF_ml3avpNZF_dhJWfFARQBiBjQW/s1600/3b-see-how-the-oauth-parameters-changed.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgplT9i6Z3Yr1n-vyrqg5p6ymFedIOTQ6_mJGvqE-oZeB4eWkedOqPScPYknskGhE6itTJCh7rtXVYYYWzgThxFh9F8lLFt40yemfCP2iZnVTCWiF3qjF_ml3avpNZF_dhJWfFARQBiBjQW/s640/3b-see-how-the-oauth-parameters-changed.PNG" /></a><br />
<br />
<br />
<h2>Authorize the Request Token</h2>Now that we have a request token, we need to <b>authorize the request token</b>. In order to authorize the request token, user interaction is required. Click the <b>Authorize </b>button, and you'll notice that a new brwoser window is popped, asking you to authorize access to your Google Buzz. This is the point where the user needs to decide if he wants to <b>give the third party access to your Google Buzz</b>. The third party in this case the Google OAuth playground, but imagine your application being mentioned ont his page.<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyuSo54MKh-psWrpY8lB_TnCrcsxH0Oh7SCQ9glG0d3tzr562Y8rJ3E2-sk-RyM8tyFcs_RCU4QYLYscFmU3M9VULzZVfojxmOGfzGvS0giAd6Kf37SEJQqnAkTCxsjDDFcODFsfLGjx5H/s1600/3c-authorize-access-through-browser.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyuSo54MKh-psWrpY8lB_TnCrcsxH0Oh7SCQ9glG0d3tzr562Y8rJ3E2-sk-RyM8tyFcs_RCU4QYLYscFmU3M9VULzZVfojxmOGfzGvS0giAd6Kf37SEJQqnAkTCxsjDDFcODFsfLGjx5H/s640/3c-authorize-access-through-browser.PNG" /></a><br />
<br />
After you've given access, your Request Token becomes <b>authorized</b>, and we can continue with the OAuth flow.<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtRQe-0O8Ictk00PyTqWRGCsTeroKAkSnPQR9RU9siQd6Er3qmVQF31x29CKHxG3-qDOaA2vhHO7eW9bUfVJvQYYOV0I56hh1Nl5zDAui3C0Jn1iJSq7LMYXz9EmRjmvPChe7V4Wa7nUh-/s1600/3d-access-token-authorized.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtRQe-0O8Ictk00PyTqWRGCsTeroKAkSnPQR9RU9siQd6Er3qmVQF31x29CKHxG3-qDOaA2vhHO7eW9bUfVJvQYYOV0I56hh1Nl5zDAui3C0Jn1iJSq7LMYXz9EmRjmvPChe7V4Wa7nUh-/s640/3d-access-token-authorized.PNG" /></a><br />
<h2>Upgrade the Authorized Token to an Access Token</h2>Our authorized access token needs to be "upgraded" to an<b> Access Token</b>. This is done without human intervention, simply by perform a call to Google with the authorized request token from the previous step.<br />
The <b>Access Token</b> is required in order to interact with the Google services.<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKtG3AR4QerjjUIWMuUKes0zZEAH5sde07pFDg9uekdHhU7LE6AM_S7fLJnkkF_xqMoJ8J2vofr9TYIXVyTicOjKe-oCQN-IdiEKqHbcSzpIWLc-BlNRu1H9QtNBjSupYbU6r-6SphxCdt/s1600/3e-access-token-retrieved.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKtG3AR4QerjjUIWMuUKes0zZEAH5sde07pFDg9uekdHhU7LE6AM_S7fLJnkkF_xqMoJ8J2vofr9TYIXVyTicOjKe-oCQN-IdiEKqHbcSzpIWLc-BlNRu1H9QtNBjSupYbU6r-6SphxCdt/s640/3e-access-token-retrieved.PNG" /></a><br />
Finally, we can start interacting with our service.The Google OAuth playground allows for an easy way to send requests to the services, and view the responses coming back. This is done in section 6 - <b>Use the token</b>. At this point, it's time to consult the <a href="http://code.google.com/apis/buzz/docs/">Google Buzz API documentation</a>.<br />
<h2>Retrieving the public Buzz feed</h2>One of the first things we'll do is retrieve the public feed of our user. This is done by putting the following URL into the feed URI field :<br />
<span class="Apple-style-span" style="font-family: monospace; white-space: pre;">https://www.googleapis.com/buzz/v1/activities/<span class="apiparam">@me</span>/@public</span><br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEim272jqrWKgh-WVx-_a77FBJPrhlc1K0eFnpC-K2I45c6REW3LU94CeL_KOvImnYrt65FKxqoRaRnYehCq-DiXPw-BXjAqyTggqHqoSv3drPuNmCnm7yJpGTc9S5T4cYTWZGd0GytVrOGV/s1600/6-use-token2.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEim272jqrWKgh-WVx-_a77FBJPrhlc1K0eFnpC-K2I45c6REW3LU94CeL_KOvImnYrt65FKxqoRaRnYehCq-DiXPw-BXjAqyTggqHqoSv3drPuNmCnm7yJpGTc9S5T4cYTWZGd0GytVrOGV/s1600/6-use-token2.PNG" style="cursor: move;" /></a><br />
<h2>Add a Buzz post</h2>In order to add a Buzz post, we need to do a POST (as opposed to a GET earlier), and enter some post data. The post data is a JSON string that represents our Buzz post.<br />
<pre>{
"data": {
"object": {
"type": "note",
"content": "Hey, this is my first Buzz Post!"
}
}
}</pre><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnGnjkYLVrtx9X4OSG5r5dwMORfE0qZYkJZhSpusiXdaOGURBd9gd5NWtt39HCJdo3WC1XblmcM0qDKtiuLZlgFIQG9QdmJEQ7vVGN-SGj3QGmaHOBx77KPPsL0p3ZhZbEO2WBDS4f6uNL/s1600/7-enter-post-data.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnGnjkYLVrtx9X4OSG5r5dwMORfE0qZYkJZhSpusiXdaOGURBd9gd5NWtt39HCJdo3WC1XblmcM0qDKtiuLZlgFIQG9QdmJEQ7vVGN-SGj3QGmaHOBx77KPPsL0p3ZhZbEO2WBDS4f6uNL/s1600/7-enter-post-data.PNG" /></a><br />
After executing our POST, check your Google Buzz to see that the post was created succesfully.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_FtP5nnVtFpryUKVW3xMMO0kO5KO6Wb09WFgtbcaVN3ifzo8kpHlAM1P83q766pObN-oQXE2aEXSOMenyxprYl5fpuv5AHTkBHG7i-xeSw4KmQkjDR4iN1zBE5mb5jdIhX9Ydy62_w5BY/s1600/8-buzz-post.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_FtP5nnVtFpryUKVW3xMMO0kO5KO6Wb09WFgtbcaVN3ifzo8kpHlAM1P83q766pObN-oQXE2aEXSOMenyxprYl5fpuv5AHTkBHG7i-xeSw4KmQkjDR4iN1zBE5mb5jdIhX9Ydy62_w5BY/s1600/8-buzz-post.PNG" /></a>ddewaelehttp://www.blogger.com/profile/00193914878294828713noreply@blogger.com2tag:blogger.com,1999:blog-1924751098502050632.post-85150594289222918722011-05-07T11:26:00.000-07:002011-05-07T13:36:47.818-07:00Street View Explorer<div dir="ltr" style="text-align: left;" trbidi="on"><div>If you thought <a href="http://maps.google.com/help/maps/streetview/" target="_blank">Google Street View</a> was pretty cool, wait untill you checkout the StreetView Explorer.</div><div>Instead of just viewing the StreetView images you can walk around in them! </div><div>Image and depth data is automatically downloaded from the official Google Street View servers.</div><div><br />
</div><div>Watch the video here :</div><div><iframe allowfullscreen="" frameborder="0" height="349" src="http://www.youtube.com/embed/EEN4DxrkXrM" width="425"></iframe></div><div><br />
</div><div>And checkout the site :</div><div><a href="http://code.google.com/p/streetview-explorer/">Streetview Explorer - View StreetView imagery in 3D</a></div></div>ddewaelehttp://www.blogger.com/profile/00193914878294828713noreply@blogger.com1tag:blogger.com,1999:blog-1924751098502050632.post-67354085982092073972011-05-06T10:54:00.001-07:002011-05-07T11:27:53.594-07:00Google URL Shortener<div dir="ltr" style="text-align: left;" trbidi="on"><h1>Introduction</h1><table border="0"><tbody>
<tr> <td><br />
In this short article, we'll be taking a look at using the Google URL shortener API, using the <a href="http://code.google.com/p/google-api-java-client/" id="project_summary_link" target="_blank">Google APIs Client Library for Java</a>.<br />
The Google URL Shortener at <a href="http://goo.gl/">goo.gl</a> is a service that takes long URLs and squeezes them into fewer characters to make a link that is easier to share, tweet, or email to friends. You can also use the Google URL Shortener API to programmatically interact with the Google URL Shortener service. This way, you can embed the service in your own applications, and use a simple REST API to store, share, and manage goo.gl short URLs from anywhere on the Internet.</td> <td><a href="http://blog.doityourselfandroid.com/wp-content/uploads/2011/05/googl.jpg"><img alt="" class="aligncenter size-medium wp-image-906" height="97" src="http://blog.doityourselfandroid.com/wp-content/uploads/2011/05/googl-300x121.jpg" title="googl" width="240" /></a></td> </tr>
</tbody> </table><br />
A good starting point for the Google URL shortener API is the <a href="http://code.google.com/apis/urlshortener/v1/getting_started.html" target="_blank" title="Google URL Shortener API">Google URL shortener API documentation</a> page. It covers authentication, and the various operations supported by the API. These operations include<br />
<ul><li>Shorten a long URL</li>
<li>Expand a short URL</li>
<li>Look up a short URL's analytics</li>
<li>Look up a user's history</li>
</ul><br />
In this article we'll focus on shortening a long URL.<br />
<br />
<h1>Setting up the project</h1><br />
We're going to be using Maven2 to handle the dependencies of our project. We add the com.google.api.client:google-api-client dependency to our pom.xml to ensure that our project has all the required dependencies.<br />
<br />
<pre class="brush: java; toolbar: false;"><project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelversion>4.0.0</modelversion>
<groupid>com.ecs.google.urlshortener</groupid>
<artifactid>urlshortener</artifactid>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>urlshortener</name>
<build>
<plugins>
<plugin>
<artifactid>maven-compiler-plugin</artifactid>
<version>2.3.2</version>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupid>com.google.api.client</groupid>
<artifactid>google-api-client</artifactid>
<version>1.2.1-alpha</version>
<exclusions>
<exclusion>
<artifactid>jsr305</artifactid>
<groupid>com.google.code.findbugs</groupid>
</exclusion>
</exclusions>
</dependency>
</dependencies></project>
</pre><br />
<h1>Setting up the HTTP Transport</h1>Now that we have the dependencies setup, we can start with the actual coding. The first thing we need to do is setup the HttpTransport. The HttpTransport will be setup using the GoogleHeaders, and we'll be specifying the application/json content-type. (as we'll be sending and receiving JSON strings over HTTP). We also configure the JSON parser on the transport to properly parse the request/response.<br />
<br />
<pre class="brush: java">// setup the anonymous Google HTTP transport.
HttpTransport transport = GoogleTransport.create();
// configure the headers on the transport.
GoogleHeaders defaultHeaders = new GoogleHeaders();
transport.defaultHeaders = defaultHeaders;
transport.defaultHeaders.put("Content-Type", "application/json");
// configure the JSON parser on the transport.
transport.addParser(new JsonHttpParser());
</pre><br />
<h1>Execute the API call</h1>Now that we have the transport setup correctly, we can execute our API call. We'll be using the https://www.googleapis.com/urlshortener/v1/url endpoint and issue a POST request.<br />
<br />
<pre class="brush: java">public static final String GOOGLE_SHORTENER_URL = "https://www.googleapis.com/urlshortener/v1/url";
// build the HTTP GET request and URL
HttpRequest request = transport.buildPostRequest();
request.setUrl(GOOGLE_SHORTENER_URL);
</pre>We'll create a JSON GenericData object, containing the longUrl that we want to shorten. We'll put the GenericData on the request.<br />
<br />
<pre class="brush: java">// Prepare the JSON data structure.
GenericData data = new GenericData();
data.put("longUrl", "http://latifymobile.com/");
JsonHttpContent content = new JsonHttpContent();
content.data = data;
// Set the JSON content on the request.
request.content = content;
</pre><h1>Parsing the response</h1>When executing this request, we can parse the HTTP response using a class we created specifically for the Google Url Shortener.<br />
<br />
<pre class="brush: java">// Execute the request, and parse the response using our Result class.
HttpResponse response = request.execute();
UrlShortenerResult result = response.parseAs(UrlShortenerResult.class);
// Print the result.
System.out.println(result.shortUrl);
</pre>As you can see, this is what we're getting as a response from the Google Shortener API.<br />
<br />
<pre class="brush: java; auto-links: false;">{
"kind": "urlshortener#url",
"id": "http://goo.gl/vl02B",
"longUrl": "http://latifymobile.com/"
}
</pre>We create a UrlShortenerResult object to parse the JSON string above, and capture the shortUrl (embedded in the id attribute).<br />
<br />
<pre class="brush: java">/**
* JSON UrlShortenerResult object, capturing the id as the shortUrl
*/
public static class UrlShortenerResult extends GenericJson {
@Key("id")
public String shortUrl;
}
</pre>And that's basically it. Our longUrl has been shortened by the API.<br />
<br />
<h1>References</h1><br />
<ul><li><a href="http://code.google.com/apis/urlshortener/v1/getting_started.html" target="_blank" title="Google URL Shortener API">Google URL shortener API documentation</a></li>
<li><a href="http://code.google.com/p/google-api-java-client/" id="project_summary_link" target="_blank">Google APIs Client Library for Java</a></li>
</ul></div>ddewaelehttp://www.blogger.com/profile/00193914878294828713noreply@blogger.com1tag:blogger.com,1999:blog-1924751098502050632.post-44468512371248382592010-08-14T06:12:00.000-07:002011-05-02T00:05:15.040-07:00Git, Github and Egit Eclipse integration<div dir="ltr" style="text-align: left;" trbidi="on">Today, I created my first repository at <a href="http://github.com/">Github</a>. The process was really simple. It basically involved the following steps :<br />
<ul><li>Signing up for a free account at <a href="http://github.com/">Github</a> and creating a repository</li>
<li>Installing git (Windows users should checkout <a href="http://code.google.com/p/msysgit/">MsysGit</a>.) to interact with the repository and GitHub.</li>
<li>Generating the key-value pairs<br />
</li>
<li>Creating a repository</li>
<li>Installing the Eclipse plugin</li>
</ul><div><a name='more'></a></div><span style="font-size: 180%;">Signing up for a free account at GitHub</span><br />
After creating your account via <a href="http://github.com/account">http://github.com/account</a>, you're presented with some information on how to continue<br />
<ul><li>with a new repository</li>
<li>existing repository</li>
</ul>Before we can start interacting with GitHub, we need to generate a private/public key-pair. This key-pair is required for allowing write access to GitHub. By providing your public key to GitHub, you'll ensure that you'll be able to push changes onto the remote GitHub repository.<br />
<br />
<span style="font-size: 180%;">Installing git</span><br />
<a href="http://git-scm.com/">Git</a> is available on a large number of platforms. <a href="http://code.google.com/p/msysgit/">MsysGit</a> provides windows based binaries for working with Git. On ubuntu, installing git is as simple as executing sudo apt-get install git-core<br />
<br />
MsysGit comes with the necessary command-line tools to generate the key-pair we just mentioned. Linux/unix users should already these tools on their system.<br />
<br />
The setup programs provides you with the necessary defaults to complete the installation in a next-next-finish kinda way.<br />
<br />
<div style="text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjepZkAT9h73De5vB0RKZEiCg-JBXJEi4fOPkHb9WnQrYNlgicTb7Cf2hE6XnSJc1G9YMpwifuNHq5mhEyrDX51V2zkraDUbmLiD4Bkf8XfBApfCNKuyti54EXRCW0L-ShkrS5AWSZDygDs/s1600/git+install1.PNG" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img alt="" border="0" id="BLOGGER_PHOTO_ID_5506045139198972802" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjepZkAT9h73De5vB0RKZEiCg-JBXJEi4fOPkHb9WnQrYNlgicTb7Cf2hE6XnSJc1G9YMpwifuNHq5mhEyrDX51V2zkraDUbmLiD4Bkf8XfBApfCNKuyti54EXRCW0L-ShkrS5AWSZDygDs/s400/git+install1.PNG" style="cursor: pointer; margin: 0px auto 10px; text-align: center;" /></a></div>Once Git is installed, it provides you with 3 programs :<br />
<ul><li>Git Bash</li>
<li>Git GUI</li>
<li>Git Uninstall</li>
</ul>The Git Bash is the shell where the git commands can be executed from. It is accesable through the start menu, and pops up window looking like this:<br />
<br />
<div style="text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgi9cDoQhxThLcguPv30iRoOyfWt_hiCZBNpWAezDqWWpt9puxv1FmcOLCpzwX1ABhYOCfxAEa7EMpBBjykUi-n7S4FODPCqbiiWPWc8BVaMQxrBy5UqmMtYbikTMrBdHk81ST2v97hnkY-/s1600/git+bash.PNG" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img alt="" border="0" id="BLOGGER_PHOTO_ID_5506045775031607970" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgi9cDoQhxThLcguPv30iRoOyfWt_hiCZBNpWAezDqWWpt9puxv1FmcOLCpzwX1ABhYOCfxAEa7EMpBBjykUi-n7S4FODPCqbiiWPWc8BVaMQxrBy5UqmMtYbikTMrBdHk81ST2v97hnkY-/s400/git+bash.PNG" style="cursor: pointer; margin: 0px auto 10px; text-align: center;" /></a><br />
Before we continue, as indicated by GitHub, and also outlined in the <a href="http://www.kernel.org/pub/software/scm/git/docs/gittutorial.html">official Git tutorial</a>, we'll need to configure our Git installation. This is done by executing the following commands in your Git shell.</div><br />
<pre>git config --global user.name "Your Name"
git config --global user.email ddewaele@gmail.com
<a href="http://github.com/account#keys">Add your public key</a></pre><br />
We can now generate our public private key pair.<br />
<br />
<span style="font-size: 180%;">Generating the public-private key pair</span><br />
An excellent guide on how this done be found <a href="http://help.github.com/msysgit-key-setup/">here</a>.<br />
<br />
It basically involves executing the following command:<br />
<pre class="brush: xml; toolbar: true;">Davy@DAVY-PC ~
$ ssh-keygen -t rsa -C "ddewaele@gmail.com"
Generating public/private rsa key pair.
Enter file in which to save the key (/c/Users/Davy/.ssh/id_rsa):
Created directory '/c/Users/Davy/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /c/Users/Davy/.ssh/id_rsa.
Your public key has been saved in /c/Users/Davy/.ssh/id_rsa.pub.
The key fingerprint is:
69:e3:96:5a:2a:98:3e:59:6b:0b:06:13:4e:a2:9d:aa my@email.com
</pre>Once your keys have been generated, make sure to keep your provide key (id_rsa) in a safe place, and upload your public key (id_rsa.pub) to GitHub.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtMVsaKLV42rjqy29dDBMwDTi1GxqxN1kn1u7apTMGodK65lBxotHp_zAc0Loi1A0V7zbpQwTybuQzvdqLZZUNcRX0sB_Y_2nsUcDi8Ixpbira9wN9U-HnvrNaGFO1oG9Wcx0XhG5QVLD7/s1600/git+install+key.PNG" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img alt="" border="0" id="BLOGGER_PHOTO_ID_5507466165817761842" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtMVsaKLV42rjqy29dDBMwDTi1GxqxN1kn1u7apTMGodK65lBxotHp_zAc0Loi1A0V7zbpQwTybuQzvdqLZZUNcRX0sB_Y_2nsUcDi8Ixpbira9wN9U-HnvrNaGFO1oG9Wcx0XhG5QVLD7/s400/git+install+key.PNG" style="cursor: pointer; height: 400px; margin: 0px auto 10px; text-align: center; width: 397px;" /></a><br />
<br />
<br />
<span style="font-size: 180%;">Installing the Eclipse plugin</span><br />
When using Eclipse as an IDE, you can use <a href="http://www.eclipse.org/egit/">EGit</a> (Eclipse Team provider for the <a href="http://git-scm.com/">Git version control system</a>.)<br />
<br />
EGit can be installed using the following download sites (Help - Install new software - Add repository)<br />
<ul><li>Main Site: <a href="http://download.eclipse.org/egit/updates">http://download.eclipse.org/egit/updates</a> (Recommended)</li>
<li>Helios Site: <a href="http://download.eclipse.org/releases/helios">http://download.eclipse.org/releases/helios</a></li>
</ul><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFKsKIX2Fz_SGQvCq1dSEAo7sn9zMuX8x1hgwyCH5Wzd0mTkbPcTZ8t3_h9okzFZvl702I0t4v3th2KofWSAOv4SM2kAngD0yv53Cl3Ljuy3ak-NrCfkFViq19YOlCTvn6EbRkcAGTyV1y/s1600/eclipse+egit+update+site.JPG" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img alt="" border="0" id="BLOGGER_PHOTO_ID_5505969658753254002" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFKsKIX2Fz_SGQvCq1dSEAo7sn9zMuX8x1hgwyCH5Wzd0mTkbPcTZ8t3_h9okzFZvl702I0t4v3th2KofWSAOv4SM2kAngD0yv53Cl3Ljuy3ak-NrCfkFViq19YOlCTvn6EbRkcAGTyV1y/s400/eclipse+egit+update+site.JPG" style="cursor: pointer; margin: 0px auto 10px; text-align: center;" /></a><br />
After installing the EGit plugin, you'll get a new perspective allowing you to interact with your Git repositories.<br />
<br />
<span style="font-weight: bold;"><span style="font-size: 180%;">Working with Egit<br />
</span></span><span style="font-size: 130%;">Configuring Eclipse SSH key management</span><br />
Before we continue, we'll configure the SSH properties in Eclipse. Once your key-pair has been created, your private key will probably reside in the ".ssh" folder of your home directory.<br />
Eclipse defines its SSH HOME to the %HOMEPATH%/ssh folder (without the 'dot'). We need to change this to point to the ".ssh" folder instead of the "ssh" folder.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2_kj3FUW1ZzvgM6niaphfmcogBx3SiukM_9gif7RoHKfUESSIO-SCSqsExICtpyobNDGs7NJ8OXjachwyWFCfQNqNLkzZz12L84_cOQyb8Tyyel152m-KPlyNVfEKrQUsIMasTEJTNuAQ/s1600/eclipse+ssh+management.PNG" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img alt="" border="0" id="BLOGGER_PHOTO_ID_5507381518682688770" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2_kj3FUW1ZzvgM6niaphfmcogBx3SiukM_9gif7RoHKfUESSIO-SCSqsExICtpyobNDGs7NJ8OXjachwyWFCfQNqNLkzZz12L84_cOQyb8Tyyel152m-KPlyNVfEKrQUsIMasTEJTNuAQ/s400/eclipse+ssh+management.PNG" style="cursor: pointer; margin: 0px auto 10px; text-align: center;" /></a><br />
<span style="font-size: 130%;">Importing projects from Egit</span><br />
To import projects from GIT repositories, we'll use the "Import Projects From Git" wizard provided in Eclipse<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhe5bEe1cnPPWD3MVLwQRI64b-f1jPhgaD3_Ysn-ICIL_ycRTrLMbE8rPG6sV0_kqxiASJlKTeD-0OzNmW7PUC-nSFSUnp-Gx7jZ6V5FqhSSNVJJgL6BlicXFe744VStA0O_-l0GpAdssgp/s1600/eclipse+import+wizard.PNG" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img alt="" border="0" id="BLOGGER_PHOTO_ID_5507407528264939538" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhe5bEe1cnPPWD3MVLwQRI64b-f1jPhgaD3_Ysn-ICIL_ycRTrLMbE8rPG6sV0_kqxiASJlKTeD-0OzNmW7PUC-nSFSUnp-Gx7jZ6V5FqhSSNVJJgL6BlicXFe744VStA0O_-l0GpAdssgp/s400/eclipse+import+wizard.PNG" style="cursor: pointer; height: 312px; margin: 0px auto 10px; text-align: center; width: 400px;" /></a><br />
Using this wizard, we can either Clone or Add a repository. We'll clone the repository that is hosted on GitHub.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjixpeVhkWZKggm7vFbs1iuWWTPBQitbkr2OQsoR8zV-6CoF_COqWqmDhUxymFUm5dZbS790ceJ1Rj_YwIi2tz1496Us_R_hGznJD5qZH5i2tAgnVEzk6SSYWdVmHHSlWmhpWNqH2q9XPO7/s1600/eclipse+import+from+git.PNG" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img alt="" border="0" id="BLOGGER_PHOTO_ID_5507406965344800258" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjixpeVhkWZKggm7vFbs1iuWWTPBQitbkr2OQsoR8zV-6CoF_COqWqmDhUxymFUm5dZbS790ceJ1Rj_YwIi2tz1496Us_R_hGznJD5qZH5i2tAgnVEzk6SSYWdVmHHSlWmhpWNqH2q9XPO7/s400/eclipse+import+from+git.PNG" style="cursor: pointer; height: 245px; margin: 0px auto 10px; text-align: center; width: 400px;" /></a><br />
We need to provide the location of the GitHub repository in the URI section<br />
<ul><li>URI : The GitHub URL, as found on your GitHub project page</li>
<li>Host : pre-filled, derived from the URI</li>
<li>Repository path : pre-filled, derived from the URI</li>
<li>Protocol : git + ssh</li>
<li>Username : should be git (no password required)</li>
</ul><br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOXgqrh3k177NnazqQAWrMscyyZu3AvkncH1LSumKhr92_DmbwoeZrr6CmuS1IvJUKD7Z15Y8xMVSFlm8ZZxpxV_2ep25XqEjntD3AgrmMlm0dQUGF9SvwMLDb8bz3CcODTqFQJxPko5KI/s1600/git+import+clone.PNG" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img alt="" border="0" id="BLOGGER_PHOTO_ID_5507406913272272162" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOXgqrh3k177NnazqQAWrMscyyZu3AvkncH1LSumKhr92_DmbwoeZrr6CmuS1IvJUKD7Z15Y8xMVSFlm8ZZxpxV_2ep25XqEjntD3AgrmMlm0dQUGF9SvwMLDb8bz3CcODTqFQJxPko5KI/s400/git+import+clone.PNG" style="cursor: pointer; height: 382px; margin: 0px auto 10px; text-align: center; width: 400px;" /></a><br />
Once a connection is established, you need to select a branch from the repo. (In this particular case, we haven't created any branches yet, and only have the master branch available).<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSbjuTWCjUMleStoVl01TlWT3IyyCitEurwpZAwmg92d31rwyedqvMxVVBgHPzadUJhv9yfygyzkML5LhbSsSjX1aZqNIwyhqyapDcD2ROOyunOzP-pN2ndhhz6nKM9mf5QXbg-NyoCcs2/s1600/git+import+clone+branch+selection.PNG" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img alt="" border="0" id="BLOGGER_PHOTO_ID_5507406830521329010" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSbjuTWCjUMleStoVl01TlWT3IyyCitEurwpZAwmg92d31rwyedqvMxVVBgHPzadUJhv9yfygyzkML5LhbSsSjX1aZqNIwyhqyapDcD2ROOyunOzP-pN2ndhhz6nKM9mf5QXbg-NyoCcs2/s400/git+import+clone+branch+selection.PNG" style="cursor: pointer; height: 382px; margin: 0px auto 10px; text-align: center; width: 400px;" /></a><br />
Next is selecting a destination for the cloned repository. Remember, when cloning a repository, you're effectively downloading an entire copy of that repository, so we need to select a local path here.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqLNATPIAShpGA9R8HxNFdh5tdw1i2eE0awNebYpiCcLVTwX6zqlhvbPGj4_3o6Lb2XM6vvtyinPqTPV_YZ0bKui7ve9wC__i2Vn6w482QQLz4-3aHV3Zg_Wq2JYnPHkv_4JFbp8WcqvfB/s1600/git+import+local+destination.PNG" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img alt="" border="0" id="BLOGGER_PHOTO_ID_5507406774246968722" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqLNATPIAShpGA9R8HxNFdh5tdw1i2eE0awNebYpiCcLVTwX6zqlhvbPGj4_3o6Lb2XM6vvtyinPqTPV_YZ0bKui7ve9wC__i2Vn6w482QQLz4-3aHV3Zg_Wq2JYnPHkv_4JFbp8WcqvfB/s400/git+import+local+destination.PNG" style="cursor: pointer; height: 382px; margin: 0px auto 10px; text-align: center; width: 400px;" /></a><br />
Once this is done, the repository will be added in the Git Repositories perspective :<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJSU7au6F1pmw62yFr-Ysl4cgXzY35bTcDVamqHCyhsyyb_DoHEKPs1MKRcpxy3vhoBtZLMp7jQMUkBjtCWtre2usZTTR2oYmQVjavVzusTnbOfVZMRDQG_GCz0AszYJc9CbQ_YMvM5Zsd/s1600/eclipse+git+repositories+explained.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img alt="" border="0" id="BLOGGER_PHOTO_ID_5507405925846209970" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJSU7au6F1pmw62yFr-Ysl4cgXzY35bTcDVamqHCyhsyyb_DoHEKPs1MKRcpxy3vhoBtZLMp7jQMUkBjtCWtre2usZTTR2oYmQVjavVzusTnbOfVZMRDQG_GCz0AszYJc9CbQ_YMvM5Zsd/s800/eclipse+git+repositories+explained.png" style="margin: 0px auto 10px; text-align: center;" /></a><br />
Once you have your GIT repository setup using Eclipse, you can start checking out your projects.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjI57G27YcBSdv9x1ZMbv0RT4CVWQE9DQ2WrDaB9EmE3Cx1pfJi9S12LlCvA9bLa0BvfLcBW4sLWNXxH3sZ5hkRsMKCxPrAxa70P6-K4DOhmr2ev94CW7251253VCoTOstCNbQkh-_3zAxH/s1600/eclipse+git+import+projects.PNG" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img alt="" border="0" id="BLOGGER_PHOTO_ID_5507414385287067234" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjI57G27YcBSdv9x1ZMbv0RT4CVWQE9DQ2WrDaB9EmE3Cx1pfJi9S12LlCvA9bLa0BvfLcBW4sLWNXxH3sZ5hkRsMKCxPrAxa70P6-K4DOhmr2ev94CW7251253VCoTOstCNbQkh-_3zAxH/s400/eclipse+git+import+projects.PNG" style="cursor: pointer; height: 239px; margin: 0px auto 10px; text-align: center; width: 400px;" /></a><br />
<br />
<span style="font-size: 130%;">Committing changes to the local using EGit</span><span style="font-weight: bold;"><br />
</span><span style="font-weight: bold;">Committing changes </span>through Eclipse is done via the Team Provider (EGit).<br />
Right-click on your project, and select <span style="font-weight: bold;">Team - Commit</span>.<br />
The following dialog screen will pop :<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj61apUI3trzDwG8Z8d3vCSgTMtRDBcCBGHMxRB0eudF73Sck1dMjhsO86f0cnLcx9rxZ4wgt0aQY42doXPdoP4TUr-hhksVkn_xPGoO5iGNt00tiqUDOV554BfSlKfHGO7CmEN3_r7Mp84/s1600/eclipse+committing+files.PNG" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img alt="" border="0" id="BLOGGER_PHOTO_ID_5507382601567978930" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj61apUI3trzDwG8Z8d3vCSgTMtRDBcCBGHMxRB0eudF73Sck1dMjhsO86f0cnLcx9rxZ4wgt0aQY42doXPdoP4TUr-hhksVkn_xPGoO5iGNt00tiqUDOV554BfSlKfHGO7CmEN3_r7Mp84/s400/eclipse+committing+files.PNG" style="cursor: pointer; margin: 0px auto 10px; text-align: center;" /></a><br />
At the bottom, it provides you with an overview of files. It also allows you to provide a <span style="font-weight: bold;">commit message</span>.Keep in mind that these commits are done on your <span style="font-weight: bold;">local GIT repository</span>. If you want to ensure that other people get to see your changes, you'll need to <span style="font-weight: bold;">push</span> them to the <span style="font-weight: bold;">remote GIT repository </span>(located at GitHub).<br />
<br />
<span style="font-size: 130%;">Pushing changes to the remote repository using EGit</span><span style="font-weight: bold;"><br />
</span>When working with<span style="font-weight: bold;"> EGit</span>, we can commit changes against our local repository, but at some point we also want to push them to the remote repository.<br />
If you want to <span style="font-weight: bold;">push </span>your changes to another repository (the one hosted on GitHub), you need to tell Egit where this repository is located. This is done using the following dialog that's popped when selecting <span style="font-weight: bold;">Team - Push</span>.<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuPIKM7lMkeajchpTu5t_BX8H-uZZRzLXBeBizE3AvsWyMv3Q6aFeIWpSrduJZJlt2GGqSMe4LlVQllT_myVf_3ZASGfEeY1nbhY0Gakl7Afnz0YMiIl-zO-fkB7quBQ-3m2xtFaj8KZ97/s1600/eclipse+pushing+to+repo.PNG" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img alt="" border="0" id="BLOGGER_PHOTO_ID_5507382497076661986" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuPIKM7lMkeajchpTu5t_BX8H-uZZRzLXBeBizE3AvsWyMv3Q6aFeIWpSrduJZJlt2GGqSMe4LlVQllT_myVf_3ZASGfEeY1nbhY0Gakl7Afnz0YMiIl-zO-fkB7quBQ-3m2xtFaj8KZ97/s400/eclipse+pushing+to+repo.PNG" style="cursor: pointer; margin: 0px auto 10px; text-align: center;" /></a><br />
The EGit shows a pre-filled remote repository. The configured remote repository is coming from your local repository configuration.<br />
When we initially cloned the remote repository using Egit, it created the following config file in your local repository<br />
<pre>[core]
repositoryformatversion = 0
filemode = true
logallrefupdates = true
autocrlf = false
bare = false
[remote "origin"]
url = git://github.com/ddewaele/Simple-Seam-Project-With-Maven.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
</pre>Unfortunately, the pre-filled remote url is not a valid URI so we need manually change it and select the Custom URI option.<br />
<br />
The correct URI can be found on the Github site. Login with your account and select the SSH url (providing read/write access). Enter that URL into the Custom URI location.<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhslnryV3vpLqd_UfbcqXM6mtypbahPPBDiMv-1Teod2Dp5xe7XuJkDt3WhqIddC9sdv6fNkLGQgl38AQV-V6VBt7qe-A_hyphenhyphenTn_UE9HDRrB7Fjlge3GmNHQzukKzn1dCDqvcaVtwXqLAnY5/s1600/github+valid+uri+modified.PNG" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img alt="" border="0" id="BLOGGER_PHOTO_ID_5507468350887038674" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhslnryV3vpLqd_UfbcqXM6mtypbahPPBDiMv-1Teod2Dp5xe7XuJkDt3WhqIddC9sdv6fNkLGQgl38AQV-V6VBt7qe-A_hyphenhyphenTn_UE9HDRrB7Fjlge3GmNHQzukKzn1dCDqvcaVtwXqLAnY5/s800/github+valid+uri+modified.PNG" style="cursor: pointer; margin: 0px auto 10px; text-align: center;" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvtjN3qN4jCraXZdKhUiHTiP7oblRPxeKR-3drrneKPoMdSC0lHIekdBZV_pbmkYl4QDs7lPENLMgAqvvqDOoN1pRuUEtLCkdxq719hdXelrAogth0uhNB7CnkPaXRpSmwJ-G5dVuSStD9/s1600/github+valid+uri.PNG" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><br />
</a><br />
So the proposed URL in the dialog box :<br />
git://github.com/ddewaele/Simple-Seam-Project-With-Maven.git<br />
<br />
Needs to be changed to this :<br />
git+ssh://git@github.com/ddewaele/Simple-Seam-Project-With-Maven.git<br />
<br />
Next step is to add the specifications we want to push.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-Q1Qrqi7Q6rkYvb3qPJEXEz-7PGBgP36uF85YvhnE9mYRRkA5N9frej-sk7QqkKvYM55GiY4SRsC5QtbLlrBl26jX568pS-fOkGXzloGgVVEpWRyEa9DI6nFjlvzNViWVZI1aVX1j3VB_/s1600/eclipse+pushing+to+repo2.PNG" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img alt="" border="0" id="BLOGGER_PHOTO_ID_5507382362369846706" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-Q1Qrqi7Q6rkYvb3qPJEXEz-7PGBgP36uF85YvhnE9mYRRkA5N9frej-sk7QqkKvYM55GiY4SRsC5QtbLlrBl26jX568pS-fOkGXzloGgVVEpWRyEa9DI6nFjlvzNViWVZI1aVX1j3VB_/s400/eclipse+pushing+to+repo2.PNG" style="cursor: pointer; margin: 0px auto 10px; text-align: center;" /></a><br />
The next dialog shows you the expected push result. What we're seeing here is that the push from our <span style="font-weight: bold;">Source Ref </span>(refs/head/master) to the <span style="font-weight: bold;">Destination Ref</span> (refs/head/master) is OK.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieIWatnp-zzpAVpzpyikdGl6vrh3L7DYlrDgu1cWjneOS9cwzlvn075A2Ebg4D7qzbTKbrSowbgiw09qQk9amidMpX3CINrGVxQilq1owtMob9iJ34E1eycTWrXtrobbkYi_9Kx4plLnEl/s1600/eclipse+pushing+to+repo3.PNG" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img alt="" border="0" id="BLOGGER_PHOTO_ID_5507382230797801570" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieIWatnp-zzpAVpzpyikdGl6vrh3L7DYlrDgu1cWjneOS9cwzlvn075A2Ebg4D7qzbTKbrSowbgiw09qQk9amidMpX3CINrGVxQilq1owtMob9iJ34E1eycTWrXtrobbkYi_9Kx4plLnEl/s400/eclipse+pushing+to+repo3.PNG" style="cursor: pointer; margin: 0px auto 10px; text-align: center;" /></a><br />
When clicking finish, we'll actually go ahead and push our changes to the remote GIT repository.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuPO5-BSAYQB4Id0Yrf38lbCnc3F68UML-aPDOjf8b7uvOuaFBhUHs5-_4hSXcT_ppnhQIes5wgPrNC6-ihm6nJDA4j_mgVEhWW4ZGcMY_9cvQFq4qtben5zWUSiSKUpXYqJFAwg56knJ0/s1600/eclipse+pushing+to+repo4.PNG" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img alt="" border="0" id="BLOGGER_PHOTO_ID_5507382123877749058" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuPO5-BSAYQB4Id0Yrf38lbCnc3F68UML-aPDOjf8b7uvOuaFBhUHs5-_4hSXcT_ppnhQIes5wgPrNC6-ihm6nJDA4j_mgVEhWW4ZGcMY_9cvQFq4qtben5zWUSiSKUpXYqJFAwg56knJ0/s400/eclipse+pushing+to+repo4.PNG" style="cursor: pointer; margin: 0px auto 10px; text-align: center;" /></a><br />
<span style="font-size: 130%;">Verifying our pushed changes on the remote repository.</span><br />
When looking at our remote repository at GitHub, we'll see our changes getting reflected there :<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhL5-E5GdCj7fjtK68DgbDlytNP33CtBpHvsaWQ8dmzIXOD3cAP2QBDIcAtgt50v_5CK4h8tW3f2mYK51d29AAIeIajwrcFRiLW6aszPLhyphenhyphenZs3jIgsLd4j_IDMFcpLYVivTSvL6A5ugqwyd/s1600/github+results+shown.PNG" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img alt="" border="0" id="BLOGGER_PHOTO_ID_5507381664306895970" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhL5-E5GdCj7fjtK68DgbDlytNP33CtBpHvsaWQ8dmzIXOD3cAP2QBDIcAtgt50v_5CK4h8tW3f2mYK51d29AAIeIajwrcFRiLW6aszPLhyphenhyphenZs3jIgsLd4j_IDMFcpLYVivTSvL6A5ugqwyd/s800/github+results+shown.PNG" style="cursor: pointer; margin: 0px auto 10px; text-align: center;" /></a><br />
References :<br />
<ul><li><a href="http://progit.org/book/">Pro Git Book</a></li>
<li><a href="http://book.git-scm.com/index.html">Git Community Book</a> </li>
<li><a href="http://www.kernel.org/pub/software/scm/git/docs/user-manual.html">Git User's Manual (for version 1.5.3 or newer)</a></li>
<li><a href="http://wiki.eclipse.org/EGit/User_Guide">EGit/User Guide</a></li>
<li><a href="http://github.com/">Github Homepage</a></li>
<li><a href="http://www.eclipse.org/egit/">EGit Homepage</a></li>
<li><a href="http://code.google.com/p/msysgit/">MsysGit Homepage</a></li>
</ul><br />
<project_dir></project_dir></div>ddewaelehttp://www.blogger.com/profile/00193914878294828713noreply@blogger.com1tag:blogger.com,1999:blog-1924751098502050632.post-15746268379133756612010-08-10T13:01:00.000-07:002011-05-02T00:06:34.227-07:00Embedding SMTP server in Seam for testing<div dir="ltr" style="text-align: left;" trbidi="on">Developîng enterprise web applictions sometimes involves implementing email functionality within the application. There is often a need to send out automated emails from the application.<br />
<br />
In a lot of cases, there is no SMTP server available to test this email communication. In the company I'm currently working for, SMTP servers are available on most non-production environments, however it's impossible to access them from a development PC. As most of the development occurs on those development PCs, the ability to test emails is vital, as the turnaround time needs to be a short as possible. (code to test).<br />
<br />
In additionan to that, email functionality doesn't lend itself well for unit testing. Offcourse it is possible to mock your email service, but at some point you also want to verify (even on a local development pc) that your email is working.<br />
<br />
<br />
<a name='more'></a><br />
<br />
<br />
Although I consider TDD one of the most valuable software development techniques, with web development we also resort to manual page testing in the browser to verify functonality. A certain use-case can involve executing business-logic, and sending out an email before being able to continue with the use-case. Obviously, we don't want get stuck at a "Cannot connect to email server" message in our browser at that point.<br />
<br />
As far as testing goes, we have the following approaches:<br />
<br />
<ul><li>Mocking in a unit test using a mocking framework like <a href="http://code.google.com/p/mockito/">Mockito</a><br />
</li>
<li>Integration testing using <a href="http://docs.jboss.com/seam/latest/reference/en-US/html/testing.html#testing.mail">SeamTest</a> to validate the email rendering<br />
</li>
<li>Using an <a href="http://code.google.com/p/subethasmtp/">embedded SMTP server</a> to verify end-to-end email functionality.</li>
</ul><br />
We'll be focussing on the third option here. In the example below I'll be using Seam (and it's built-in email functionality) as a test-case for an embedded SMTP server. The advantage of using Seam is that we can make full use of email templating using xhtml pages. See <a href="http://docs.jboss.org/seam/2.2.1.CR1/reference/en-US/html/mail.html">the email section in the Seam manual</a> for more information on this.<br />
<br />
<span style="font-size: 180%;">Setting up the environment</span><br />
<span style="font-size: 130%;">Configuring Tomcat - Email libraries</span><br />
As discussed <a href="http://tomcat.apache.org/tomcat-5.5-doc/jndi-resources-howto.html">here</a>, in the Install the JavaMail libraries section, you need the following 2 JARs in $CATALINA_HOME/common/lib<br />
<ul><li>mail.jar<br />
</li>
<li>activation.jar<br />
</li>
</ul><span style="font-size: 130%;">Configuring Tomcat - Context</span><br />
Defining the following element will register the JavaMail Session.<br />
<pre class="brush: xml"><resource name="mail/Session" auth="Container" type="javax.mail.Session" host="smtp.server">
</resource></pre><br />
Later on, we'll see how our application will this resource through a web.xml entry.<br />
<pre class="brush: xml"><resource-ref>
<res-ref-name>mail/Session</res-ref-name>
<res-type>javax.mail.Session</res-type>
<res-auth>Container</res-auth>
</resource-ref></pre><br />
<span style="font-size: 130%;">Configuring Seam - components.xml</span><br />
<pre class="brush: xml"><mail:mail-session session-jndi-name="java:comp/env/mail/Session"/></pre><br />
<span style="font-size: 130%;">Seam Email component</span><br />
Our Seam email component can be injected in any other Seam component. When invoking the sendMail method, the correct template (xhtml page) will be rendered.<br />
<pre class="brush: java">import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.faces.FacesMessages;
import org.jboss.seam.faces.Renderer;
import be.telenet.releasemgmt.pmc.domain.communication.MailService;
@Name("mailService")
public class SeamMailService implements MailService {
@In(create = true)
private Renderer renderer;
@In
private FacesMessages facesMessages;
public void sendMail(String template) {
try {
renderer.render(template);
facesMessages.add("Email sent successfully");
} catch (Exception e) {
facesMessages.add("Email sending failed: " + e.getMessage());
}
}
}</pre><br />
<span style="font-size: 130%;">Seam Email Template</span><br />
The Seam email template is an xhtml page, allowing us to use the same templating technology as we use for our web pages. We have full access to the Seam scopes (via EL expressions) in these templates.<br />
<pre class="brush: xml"><m:message xmlns="http://www.w3.org/1999/xhtml"
xmlns:m="http://jboss.com/products/seam/mail"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<ui:repeat value="#{recipients}" var="recipient">
<m:to name="#{recipient.userId}" address="#{recipient.email}" />
</ui:repeat>
<m:from name="#{sender.userId}" address="#{sender.email}" />
<m:subject>Email Title</m:subject>
<m:body>
<h1>Email body, feel free to use HTML tags and EL Expressions here</h1>
</m:body>
</m:message></pre><br />
<span style="font-size: 130%;">Wiser Smtp Server Bootstrapper</span><br />
We'll be using <a href="http://code.google.com/p/subethasmtp/wiki/Wiser">Wiser</a> as the embedded SMTP server. We'll wrap it in a Seam component to have proper lifecycle management (start/stop), and also use it later on to have a view on the message processed by the SMTP server.<br />
<ul><li>Starts the SMTP server (on port 25) after Seam startup.<br />
</li>
<li>Stops SMTP server when the Seam Application Context is released. </li>
<li>Provides access to the messages in our SMTP server</li>
<li>Contains a helper method to display the body of the message</li>
</ul><br />
<pre class="brush: java">@Name("wiserSmtpServer")
@Scope(ScopeType.APPLICATION)
public class WiserSmtpBootstrapper {
private Wiser wiser;
public Wiser getWiser() {
return wiser;
}
@Observer("org.jboss.seam.postInitialization")
public void startSmtpServer() {
this.wiser = new Wiser(25);
this.wiser.start();
}
@Destroy
public void stopSmtpServer() {
this.wiser.stop();
}
public List<WiserMessage> getMessages() {
return this.wiser.getMessages();
}
public String getBody(byte[] content) {
return new String(content);
}
}</pre><br />
<br />
<br />
<span style="font-size: 130%;">Display email messages</span><br />
Our SMTP Wrapper component (Seam component) that is put in application scope is also used to display the actual email messages.<br />
<pre class="brush: xml"><rich:dataTable id="emailList" value="#{wiserSmtpServer.messages}" var="email"
onRowMouseOver="this.style.backgroundColor='#F1F1F1'"
onRowMouseOut="this.style.backgroundColor='#{a4jSkin.tableBackgroundColor}'">
<rich:column>
<f:facet name="header">
<h:outputText value="#{messages.email_from}" />
</f:facet>
<h:outputText value="#{email.envelopeSender}" />
</rich:column>
<rich:column>
<f:facet name="header">
<h:outputText value="#{messages.email_to}" />
</f:facet>
<h:outputText value="#{email.envelopeReceiver}" />
</rich:column>
<rich:column>
<f:facet name="header">
<h:outputText value="#{messages.email_subject}" />
</f:facet>
<h:outputText value="#{email.mimeMessage.subject}" />
</rich:column>
<rich:column>
<f:facet name="header">
<h:outputText value="#{messages.email_body}" />
</f:facet>
<h:outputText value="#{wiserSmtpServer.getBody(email.data)}" escape="false" />
</rich:column>
</rich:dataTable></pre></div>ddewaelehttp://www.blogger.com/profile/00193914878294828713noreply@blogger.com2tag:blogger.com,1999:blog-1924751098502050632.post-56937442249473538762010-07-25T04:09:00.000-07:002010-07-25T04:10:06.820-07:00Integrating SyntaxHighlighter on blogger.comToday I integrated SyntaxHighlighter (<a href="http://alexgorbatchev.com/SyntaxHighlighter">http://alexgorbatchev.com/SyntaxHighlighter</a>) on my blog. Adding source code to a blog works like a charm by embedding it in <pre></pre> tags.<br /><br /><pre class="brush: java"><br />-- insert code here ---<br /></pre><br /><br />In order for SyntaxHighlighter to work, you need to to the following in your blogger.com account<br /><br />Just after the <head> tag:<br /><pre class="brush: xml; toolbar: true;"><link href='http://alexgorbatchev.com/pub/sh/current/styles/shCore.css' rel='stylesheet' type='text/css'/><br /><link href='http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css' rel='stylesheet' type='text/css'/><br /><script src='http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js' type='text/javascript'/><br /><script src='http://alexgorbatchev.com/pub/sh/current/scripts/shAutoloader.js' type='text/javascript'/><br /><script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJava.js' type='text/javascript'/><br /><script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushXml.js' type='text/javascript'/></pre><br />Just before the </body> tag :<br /><pre class="brush: xml"><script type='text/javascript'><br />SyntaxHighlighter.config.bloggerMode = true;<br />SyntaxHighlighter.all();<br /></script></pre><br /><br />One minor issue encountered with XML rendering. This doesn't seem to work properly unless replacing all left brackets with <<br />So currently I'm stuck with copying the code to an editor, executing a search "<" and replace with &lt; XML Snippet with left handed brackets included <br /><pre class="brush: xml"><persistence><br /><persistence-unit name="pu" type="RESOURCE_LOCAL"><br /><provider>org.hibernate.ejb.HibernatePersistence</provider><br /><class>be.ecs.validation.entities.Person</class><br /><class>be.ecs.validation.entities.City</class><br /><class>be.ecs.validation.entities.Country</class><br /><properties><br /> <property name="hibernate.connection.url" value="jdbc:hsqldb:mem:unit-testing-jpa"><br /> <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver"><br /> <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"><br /> <property name="hibernate.hbm2ddl.auto" value="create-drop"><br /> <property name="hibernate.connection.username" value="sa"><br /> <property name="hibernate.connection.password" value="x"><br /><property name="hibernate.archive.autodetection" value="class, hbm"><br /></property></property></property></property></property></property></property></properties></persistence-unit></persistence></pre><br /><br /><br />XML Snippet with left handed brackets replaced with &lt;<br /><br /><pre class="brush: xml"><persistence><br /><persistence-unit name="pu" transaction-type="RESOURCE_LOCAL"><br /><provider>org.hibernate.ejb.HibernatePersistence</provider><br /><class>be.ecs.validation.entities.Person</class><br /><class>be.ecs.validation.entities.City</class><br /><class>be.ecs.validation.entities.Country</class><br /><properties><br /> <property name="hibernate.connection.url" value="jdbc:hsqldb:mem:unit-testing-jpa"/><br /> <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver"/><br /> <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/><br /> <property name="hibernate.hbm2ddl.auto" value="create-drop"/><br /> <property name="hibernate.connection.username" value="sa"/><br /> <property name="hibernate.connection.password" value="x"/><br /><property name="hibernate.archive.autodetection" value="class, hbm"/><br /></properties><br /></persistence-unit><br /></persistence></pre><br />Rest seems to be working fine...<br /><pre class="brush: java"><br />package be.ecs.seamdemo.beans;<br /><br />import javax.persistence.EntityManager;<br /><br />import org.jboss.seam.annotations.In;<br />import org.jboss.seam.annotations.Name;<br />import org.jboss.seam.framework.EntityHome;<br /><br />import be.ecs.seamdemo.entities.Category;<br /><br />@Name("categoryHome")<br />public class CategoryHome extends EntityHome<category> {<br /><br />private static final long serialVersionUID = 1729733881106540912L;<br /><br />@In<br />EntityManager entityManager;<br /><br />public EntityManager getEntityManager() {<br />return entityManager;<br />}<br /><br />@Override<br />public String persist() {<br />super.persist();<br />return null;<br />}<br />}<br /></category><br /><br /><br /><br /></pre>ddewaelehttp://www.blogger.com/profile/00193914878294828713noreply@blogger.com2tag:blogger.com,1999:blog-1924751098502050632.post-54507159547241735302010-07-24T08:24:00.000-07:002010-07-25T03:46:48.912-07:00Unit Testing your JPA entitiesIf you want to avoid exceptions like this at runtime after adding properties to your JPA entities, it might be worthwhile to invest some time in writing unit tests for them.<br /><pre class="brush: xml">Caused by: org.hibernate.MappingException: Could not determine type for: be.ecs.validation.entities.City, at table: Person, for columns: [org.hibernate.mapping.Column(city)]<br />at org.hibernate.mapping.SimpleValue.getType(SimpleValue.java:291)<br />at org.hibernate.tuple.PropertyFactory.buildStandardProperty(PropertyFactory.java:143)<br />at org.hibernate.tuple.component.ComponentMetamodel.<init>(ComponentMetamodel.java:68)<br />at org.hibernate.mapping.Component.buildType(Component.java:183)<br />at org.hibernate.mapping.Component.getType(Component.java:176)<br />at org.hibernate.mapping.SimpleValue.isValid(SimpleValue.java:275)<br />at org.hibernate.mapping.Property.isValid(Property.java:217)<br />at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:464)<br />at org.hibernate.mapping.RootClass.validate(RootClass.java:236)<br />at org.hibernate.cfg.Configuration.validate(Configuration.java:1193)<br />at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1378)<br />at org.hibernate.cfg.AnnotationConfiguration.buildSessionFactory(AnnotationConfiguration.java:954)<br />at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:891)<br />... 43 more<br /></init></pre><br />The Spring TestContext Framework provides generic, annotation-driven unit and integration testing support. More information can be found on http://static.springsource.org/spring/docs/3.0.3.RELEASE/spring-framework-reference/html/testing.html<br /><br />The following needs to be done<br /><ol><li>Create a persistence.xml specific to your unit tests</li><li>Create a Spring application context to bootstrap the entitymanager</li><li>Create the unit test that </li></ol><ul><li>constructs an entity</li><li>persists it</li><li>retrieves it</li></ul><ol></ol><span style="font-size:130%;">1. Create a persistence.xml specific to your unit tests</span><br />We'll create a dedicated persistence.xml file in our package (src/test/java/META-INF/persistence.xml). In this example, we're using an in-memory HSQL database.<br /><pre class="brush: xml"><br /><persistence><br /> <persistence-unit name="pu" transaction-type="RESOURCE_LOCAL"><br /> <provider>org.hibernate.ejb.HibernatePersistence</provider><br /> <class>be.ecs.validation.entities.Person</class><br /> <class>be.ecs.validation.entities.City</class><br /> <class>be.ecs.validation.entities.Country</class><br /> <properties><br /> <property name="hibernate.connection.url" value="jdbc:hsqldb:mem:unit-testing-jpa"/><br /> <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver"/><br /> <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/><br /> <property name="hibernate.hbm2ddl.auto" value="create-drop"/><br /> <property name="hibernate.connection.username" value="sa"/><br /> <property name="hibernate.connection.password" value="x"/><br /> <property name="hibernate.archive.autodetection" value="class, hbm"/><br /> </properties><br /> </persistence-unit><br /></persistence><br /></pre><br /><br /><br /><span style="font-size:130%;">2. Create a Spring application context</span><br /><br />xxx<br /><pre class="brush: xml"><br /><beans><br /><br /> <bean id="entityManagerFactory"<br /> class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean"><br /> <property name="persistenceUnitName" value="pu" /><br /> </bean><br /> <br /> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"><br /> <property name="entityManagerFactory" ref="entityManagerFactory" /><br /> </bean><br /></beans><br /></pre><br /><br /><span style="font-size:130%;">3. Create the unit test</span><br /><pre class="brush: java"><br />@RunWith(SpringJUnit4ClassRunner.class)<br />@ContextConfiguration(locations={"/applicationContext.xml"})<br />public class PersonPersistenceTest {<br /><br />@PersistenceContext<br />private EntityManager entityManager;<br /><br />public EntityManager getEntityManager() {<br />return entityManager;<br />}<br /><br />@Test<br />public void createAndPersistPerson() {<br />Person person = new Person();<br />getEntityManager().persist(person);<br />Person retrievedPerson = getEntityManager().find(Person.class, person.getId());<br />Assert.assertNotNull(retrievedPerson);<br />}<br /><br />}<br /></pre>ddewaelehttp://www.blogger.com/profile/00193914878294828713noreply@blogger.com1tag:blogger.com,1999:blog-1924751098502050632.post-48961926189684133002010-07-24T06:15:00.000-07:002010-07-25T13:49:06.004-07:00Seam returns HTTP Status 404 but the page existDid it ever occur to you that you saw this message while are sure that particular page does exist at the given location ? :<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTEsQaVXpFlqy5KKsQPCENZjUWBH_ztbH8RKUoxVpqmoZtqj6cDsdxqZ5daU8qpuMJXg0NJ0OZ9m0QI8BH9hDi8mjC_JJHwegE7ub4Vs59DJdavWl4atWd7j_8oICcH99yHTdoH40QRFbj/s1600/Error+404+due+to+missing+xhtml+template.PNG"><img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 194px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTEsQaVXpFlqy5KKsQPCENZjUWBH_ztbH8RKUoxVpqmoZtqj6cDsdxqZ5daU8qpuMJXg0NJ0OZ9m0QI8BH9hDi8mjC_JJHwegE7ub4Vs59DJdavWl4atWd7j_8oICcH99yHTdoH40QRFbj/s400/Error+404+due+to+missing+xhtml+template.PNG" alt="" id="BLOGGER_PHOTO_ID_5497462826861910226" border="0" /></a><br />Always keep an eye on the Seam console output, as in this case it provides the following info :<br /><pre><br /><span style="color: rgb(255, 0, 0);">Jul 24, 2010 3:14:46 PM com.sun.facelets.impl.DefaultFaceletFactory createFacelet</span><br /><span style="color: rgb(255, 0, 0);">WARNING: /conversations/template/template.xhtml not found at jndi:/localhost/seamdemo.ui/conversations/template/template.xhtml</span><br /></pre>The root cause for the 404 is the fact that his particular page referred to a template that couldn't be located :<br /><pre class="brush: xml"><br /><ui:composition xmlns="http://www.w3.org/1999/xhtml" <br />s="http://jboss.com/products/seam/taglib" <br />ui="http://java.sun.com/jsf/facelets" <br />f="http://java.sun.com/jsf/core" <br />h="http://java.sun.com/jsf/html" <br />rich="http://richfaces.org/rich" <br />template="template/template.xhtml"><br /><ui:define name="body"><br /> ....<br /> </ui:define><br /></ui:composition></pre><br />As you can see, the template (defined in a relative way) couldn't be located from the page consuming it (located in a different folder).ddewaelehttp://www.blogger.com/profile/00193914878294828713noreply@blogger.com3