While the Java client library for Google DATA API can’t be compiled by the
GWT compiler, you can still use it in RPC Servlets with the advantage of hiding
the protocol or substituting the default authentication backend with yours.
The screenshot below shows an example of a simple GWT application made up of
a tree widget displaying my spreadsheets and their worksheets, and a grid
widget on which appears the selected worksheet.
The source code of this demo is at the bottom.
Google Data APIs are based on Atom and RSS formats and the Atom Publishing
Protocol. Since they are REST-styled, you may use them with any browser:
First we need to authenticate to the service we want to use (Google
Spreadsheets in our case) with our Google account. The easiest way is to use
the ClientLogin interface:
$ export TOKEN=`curl https://www.google.com/accounts/ClientLogin \
-d Email=cappelle.florent@gmail.com -d Passwd=$GDATA_PASSWORD \
-d accountType=HOSTED_OR_GOOGLE \
-d source=SFEIR-GWTSheets-1 \
-d service=wise | grep 'Auth=' | cut -d'=' -f2`
The service parameter is the Google service we want to use; in this case the
«wise» value stands for the spreadsheets service. If the login is successful,
the response will include a token which we will use in the next requests. Note
that in some cases the service may require a CAPTCHA challenge to be done.
Then we request the list of the spreadsheets we include our token in the
Authorization header:
$ curl -H "Authorization: GoogleLogin auth=$TOKEN" \
"http://spreadsheets.google.com/feeds/spreadsheets/private/full" > SpreadsheetList.xml
Querying and editing our spreadsheets this way (you would have to build PUT
requests with XML) would be quite inefficient. Instead of parsing XML and
building HTTP requests, you may use the Java client library which already does
this for you with the SpreadsheetService class:
1 SpreadsheetService sService;
2 sService = new SpreadsheetService("SFEIR-GWTSheets-1");
3 try
4 {
5 sService.setUserCredentials(email, password);
6 logger.info(email + " authentication successful");
7 getSession().setAttribute("sService", sService);
8 } catch (AuthenticationException e)
9 {
10 logger.log(Level.WARNING, e.getMessage());
11 thrownew AuthException(e.getMessage());
12 }
The preceding piece of code is extracted from a GWT-RPC Service method:
- The «setUserCredentials» method requests a token and stores it in the
sService object, which we store in the session for the next requests.
- If the login fails or a CAPTCHA challenge is needed, an AuthException is
thrown back to the client.
Once the user is logged in, we build a feed URL with the FeedURLFactory
class (see the following piece of code). Its «getSpreadsheetsFeedUrl» returns
the URL for the spreadsheet list feed, which then is fetched by the
SpreadsheetService object (sService).
1 List<SpreadsheetEntry> spreadsheetEntries = null;
2 FeedURLFactory factory = FeedURLFactory.getDefault();
3 SpreadsheetFeed feed;
4
5 try
6 {
7 feed = service.getFeed(factory.getSpreadsheetsFeedUrl(),
8 SpreadsheetFeed.class);
9 spreadsheetEntries = feed.getEntries();
10 getSession().setAttribute("spreadsheetEntries",
11 spreadsheetEntries);
12 } catch (IOException e) ...
The «getEntries» method returns a list of SpreadsheetEntry: Each of these
entries contains standard Atom information (author, title, etc) and a worksheet
list feed URL. However we don’t even need to build a feed URL since the
«getWorksheets» method directly returns a list of WorksheetEntry.
The SpreadSheetModel is a list of Worksheet which is returned to the GWT
client: the title and the dimensions of each worksheet are used by the tree and
the grid widgets. The cell feed URL is also needed to fetch the content of each
cell.
1 SpreadSheetModel model = new SpreadSheetModel();
2 try
3 {
4 List<WorksheetEntry> worksheetEntries =
5 entry.getWorksheets();
6 for (WorksheetEntry sheet : worksheetEntries)
7 {
8 model.add(new SpreadSheetModel.WorkSheet(
9 sheet.getTitle().getPlainText(),
10 sheet.getCellFeedUrl().toExternalForm(),
11 sheet.getRowCount(), sheet.getColCount()));
12 }
13 } catch (IOException e) ...
At the client side, the code that fills the tree widget is pretty
simple:
1 public void onSuccess(SpreadSheetModel model)
2 {
3 TreeItem item = getItem(position);
4 for (SpreadSheetModel.WorkSheet sheet : model)
5 {
6 TreeItem newItem = item.addItem(sheet.getTitle());
7 newItem.setUserObject(sheet);
8 }
9 // remove the empty child (see populate method)
10 item.getChild(0).remove();
11 item.setState(true, false);
12 }
Once the user clicks on a worksheet title in the tree, the worksheet feed
URL is given to a RPC method «fetchworksheetcells», which returns a list of
cells: the Cell class of the Google API can’t be used as it is in the client
side, so we have to copy its properties to a DTO class (WorksheetCell).
«copyProperties» of the Apache Commons Beanutils library does this in a single
line.
1 List<WorksheetCell> cells = new ArrayList<WorksheetCell>();
2 try
3 {
4 CellFeed feed = sService.getFeed(new URL(sheetFeedURL),
5 CellFeed.class);
6 List<CellEntry> cellEntries = feed.getEntries();
7 for (CellEntry entry : cellEntries)
8 {
9 Cell cell = entry.getCell();
10 WorksheetCell clonedCell = new WorksheetCell();
11 BeanUtils.copyProperties(clonedCell, cell);
12 cells.add(clonedCell);
13 }
14 } catch (MalformedURLException e) ...
Note that worksheet feeds also have a list feed, which enables relational
alike manipulation of worksheet data. Insertion, modification and deletion are
very easy with the Java client, for example the modification of a cell entry
needs two lines of code:
1 cellEntry.changeInputValueLocal(newValue);
2 cellEntry.update();
Useful Links:
Article related downloads: