This series explores application architectures that use a Service Oriented Architecture (SOA) on the back end implemented with the Grails framework. Explore how much Grails simplifies creating Web applications in general and Web services in particular. This kind of back end can be easily hooked up to any pure client-side application. In Part 1, you used Adobe Flex to create an application that leveraged the Flash Player. In this article you will use the Google Web Toolkit (GWT) to create the front end in pure JavaScript.
In this article you will build a Web application using Grails and GWT. The Grails framework is built on the Groovy programming language, a dynamic language for the Java™ platform. Familiarity with Groovy is great, but not completely necessary. Knowledge of Java programming can be a good substitute, or even other dynamic languages like Ruby or Python. Grails 1.0.3 was used with developing this article (see Resources). Grails can work with numerous databases or application servers, but none is needed for this article—Grails comes with both. The front end is built using the Google Web Toolkit, and to use GWT you definitely must be familiar with Java. Google Web Toolkit 1.5.3 was used for this article (see Resources).
In Part 1, you created an application that used a Service Oriented Architecture (SOA) built using Grails. It is possible to build a new application on top of this back end. That is one of the major advantages of using an SOA back end with a pure client-side front end. Your server-side code is completely decoupled from the user interface. However, to further demonstrate how easy it is to create Web services with Grails, let's modify the existing back end and add a search API to it.
The existing application had a search business service. We only used its list API to provide a list of all stories in the application. Take a look at what else the service can do, as shown in Listing 1.
Listing 1. The Search Business service
class SearchService {
boolean transactional = false
def list() {
Story.list()
}
def listCategory(catName){
Story.findAllWhere(category:catName)
}
def searchTag(tag){
Story.findAllByTagsIlike("%"+tag+"%")
}
}
|
The
listCategory method finds all of the stories in
a given category. The searchTag method finds all of
the stories with a given tag. It does not require an exact match and is case
insensitive. As mentioned, we have only used the list API so far. So let's
create a new Web service that uses these other two methods. All you need to do
is add a new method to the ApiController, as shown
in Listing
2.
Listing 2. New
ApiController
import grails.converters.*
class ApiController {
// injected services
def searchService
def storyService
def search = {
def results= null
def tagResults = null
if (params.tag){
tagResults = searchService.searchTag(params.tag)
}
def catResults = null
if (params.category){
catResults = searchService.listCategory(params.category)
}
if (params.tag && params.category){
def tagMap = [:]
tagResults.each{ story ->
tagMap[story.id] = story
}
results = catResults.findAll { tagMap[it.id] != null}
} else {
if (params.category){
results = catResults
} else {
results = tagResults
}
}
render results as JSON
}
}
|
Listing
2 only shows the new search method. There are two request parameters that it
expects: tag and category. It can handle either or both. These parameters, like all
request parameters in a Grails application, are exposed through the params object. If the tag
parameter is present, then the searchTag method is
invoked on the search service. If the category
parameter is present, then the listCategory method
is invoked on the search service. If both parameters are present (if params.tag and params.category), then things get interesting.
We only want
to show the stories that are common to both lists of stories returned from the
calls to the search service. So first you create an empty map using the [:]
notation. This is a HashMap from Java. You then iterate over the list from the
stories with the given tag, putting each into the map using the ID of the
story as the key. Use the standard Groovy
syntax for a closure. Then you find all of the stories from the category
search whose ID is present in the map you just created. Use the convenient
findAll method that Groovy adds to lists, and
once again use a closure. This time use Groovy's shorthand notation (the 'it'
object) for the closure. Finally, whatever the results are, use the JSON
converter from Grails to turn the list into a JSON object.
In the previous article (see Resources), you saw how easy Grails makes it to render data as XML, but as you can see, Grails makes it just as easy to render as JSON. You can now test the new Web service. Following the Grails conventions, you know the URL to it will be http://<root>/digg/api/search. Here is some sample output for doing a search where category is equal to "technology," as shown in Listing 3.
Listing 3. Sample search output
[{"id":1,"class":"Story","category":"technology","description":"How to get
a ternary operator in Scala","link":"http://blog.tmorris.net/does-scala-have-
javas-ternary-operator/","tags":"programming scala","title":"Does Scala have
Java's ternary operator?","votesAgainst":0,"votesFor":0},{"id":3,"class":"Story",
"category":"technology","description":"New animations available in the Flex 4 'Gumbo'
release.","link":"http://graphics-geek.blogspot.com/2008/10/flex-specificanimations
-posted.html","tags":"flash flex","title":"Flex Specific Animations Posted",
"votesAgainst":0,"votesFor":0}]
|
This
is standard JSON and is exactly what you would expect. You get an
array of objects, where each JSON object represents a story. One interesting
thing you should notice is that the Grails serializer adds a class attribute that is the Groovy class of your
object. You will not need this in your application here because the list is
homogeneous, but you could imagine scenarios where this metadata would be very
useful. Now that you've modified the Web services, you can start building a
new application that consumes this service using the GWT.
We are ready to create a new application for searching stories from the Digg clone application. Like the Flex-based application, this will be a completely client-side application that will call your Grails-powered services. This time, however, you will use the GWT. This allows you to program all of your application in Java, but GWT will compile it into extremely efficient JavaScript that will run on the client. Let's start by looking at the entry point to the GWT-powered application.
The entry point into a GWT application is a Java class whose compiled JavaScript is the script for a given page. It is responsible for creating the user interface and handling interactions. In this case you need to create a form the user can fill out to specify a search query, as well the result of those queries. Take a look at this code in Listing 4.
Listing 4. The
DiggApp class
public class DiggApp implements EntryPoint {
private static final String[][] CATEGORIES = {
{ "Technology", "technology" }, { "World & Business", "business" },
{ "Science", "science" }, { "Gaming", "games" },
{ "Lifestyle", "lifestyle" }, { "Entertainment", "entertainment" },
{ "Sports", "sports" }, { "Offbeat", "miscellaneous" } };
private final HorizontalPanel searchPanel = createSearchForm();
private final VerticalPanel resultsPanel = new VerticalPanel();
public void onModuleLoad() {
RootPanel.get().add(searchPanel);
RootPanel.get().add(resultsPanel);
}
private HorizontalPanel createSearchForm() {
HorizontalPanel panel = new HorizontalPanel();
panel.setTitle("Search for Stories");
Label tagLabel = new Label("Tag:");
final TextBox tagBox = new TextBox();
panel.add(tagLabel);
panel.add(tagBox);
Label catLabel = new Label("Category:");
final ListBox catBox = new ListBox();
catBox.addItem("");
for (String[] category : CATEGORIES){
catBox.addItem(category[0],category[1]);
}
panel.add(catLabel);
panel.add(catBox);
Button searchBtn = new Button("Search");
searchBtn.addClickListener(new ClickListener(){
public void onClick(Widget sender) {
search(tagBox.getText(), catBox.getValue(catBox.getSelectedIndex()));
}
});
panel.add(searchBtn);
return panel;
}
}
|
The
onModuleLoad method is invoked when the page is
loaded. In this case it creates a search form with a text box for entering
tags, a drop-down list of categories, and a search button for requesting a
search. It also creates an empty panel for showing search results. The search
method is invoked when the search button is clicked. The code for that is
shown in Listing
5.
Listing 5. The
DiggApp search method
private final void search(String tag, String category){
resultsPanel.clear();
Story.search(tag, category, new RequestCallback(){
public void onError(Request request, Throwable exception) {
Label label = new Label("Sorry there was an error");
resultsPanel.add(label);
}
public void onResponseReceived(Request request, Response response) {
List<Story> stories = Story.fromJson(response.getText());
SearchTable grid = new SearchTable(stories);
resultsPanel.add(grid);
}
});
}
|
This
uses a search method on the Story class. The Story class acts as the model in the system, very
similar to the Story class created in the Flex
application from the first part of this series. It has a static search
method and acts as a data model. It is shown in Listing
6.
Listing 6. The
Story class
public class Story {
private static final String BASE_URL = "/digg/api/search?";
private int id;
private String link;
private String title;
private String description;
private String tags;
private String category;
private int votesFor;
private int votesAgainst;
public Story(JSONObject obj){
id = (int) obj.get("id").isNumber().doubleValue();
link = obj.get("link").isString().stringValue();
title = obj.get("title").isString().stringValue();
description = obj.get("description").isString().stringValue();
tags = obj.get("tags").isString().stringValue();
category = obj.get("category").isString().stringValue();
votesFor = (int) obj.get("votesFor").isNumber().doubleValue();
votesAgainst = (int) obj.get("votesAgainst").isNumber().doubleValue();
}
public static void search(final String tag, final String category, final
RequestCallback callback){
String url = buildRequest(tag, category);
RequestBuilder builder =
new RequestBuilder(RequestBuilder.GET, url);
try {
builder.sendRequest(null, new RequestCallback(){
public void onError(Request request, Throwable exception) {
callback.onError(request, exception);
}
public void onResponseReceived(Request request, Response response) {
callback.onResponseReceived(request, response);
}
});
} catch (RequestException e) {
callback.onError(null, e);
}
}
}
|
Note
that this is only a partial listing of the class; some helper methods and the
getters and setters have been omitted. The search method takes the tag and
category and also a RequestCallback. It then uses
the GWT RequestBuilder class to create and send an
HTTP request to the Web service. RequestCallback is
an interface in GWT needed for sending asynchronous HTTP requests. The Story.search method simply delegates the RequestCallback passed in to it. So in this case it
will execute the RequestCallback created in Listing
5. Note that in both Listing 5 and Listing 6, we created an anonymous inner
class that implemented the RequestCallback
interface. This is a common technique in GWT (and in Java UI programming in
general) because it gives you access to the fields and methods of the
enclosing class. Going back to Listing
5 again, you can see that you get the
response text and parse it into a list of Story
objects using the Story.fromJson method. This is
shown in Listing
7.
Listing 7. The
Story.fromJson method
public static List<Story> fromJson(String jsonText){
JSONValue val = JSONParser.parse(jsonText);
JSONArray array = val.isArray();
List<Story> stories = new ArrayList<Story>(array.size());
for (int i=0;i<array.size();i++){
Story story = new Story(array.get(i).isObject());
stories.add(story);
}
return stories;
}
|
This
method uses GWT's JSONParser class. It creates an
array of JSON objects and passes each object into the constructor of the Story class (shown in Listing
6). Going back to
Listing
5 again, you see that once you have the list of story objects, you use
it to create a SearchTable object. This is a custom
widget and is shown in Listing
8.
Listing 8. The SearchTable widget
public class SearchTable extends FlexTable {
private List<Story> stories;
public SearchTable(List<Story> stories) {
super();
this.stories = stories;
this.buildTable();
}
private void buildTable(){
this.setBorderWidth(2);
this.setText(0, 0, "story");
this.setText(0, 1, "category");
this.setText(0, 2, "description");
if (stories.size() == 0){
showMessage("Sorry there were no results");
} else {
for (int i=0;i<stories.size();i++){
Story story = stories.get(i);
setWidget(i+1, 0, story.getTitleLink());
setText(i+1, 1, story.getCategory());
setText(i+1, 2, story.getDescription());
}
}
}
private void showMessage(String msg){
setText(1,0, msg);
getFlexCellFormatter().setColSpan(1, 0, 3);
}
}
|
This
class extends the core GWT class, FlexTable. It
simply iterates over the list of stories and populates a simple table. If
there are no stories, then it shows a simple message saying as much. You now
have created all of the code for the application, but there is one more thing
you need to do. The Story class used GWT's HTTP
library and JSON library for sending requests to the Web service and for
parsing the response from the service. You need to make these available to the
compiler by adding it to the module definition for the application. This is
shown in Listing
9.
Listing 9. The module definition
<module>
<!-- Inherit the core Web Toolkit stuff. -->
<inherits name='com.google.gwt.user.User'/>
<!-- Inherit the default GWT style sheet. You can change -->
<!-- the theme of your GWT application by uncommenting -->
<!-- any one of the following lines. -->
<inherits name='com.google.gwt.user.theme.standard.Standard'/>
<!-- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/> -->
<!-- <inherits name='com.google.gwt.user.theme.dark.Dark'/> -->
<!-- Other module inherits -->
<inherits name="com.google.gwt.http.HTTP" />
<inherits name="com.google.gwt.json.JSON" />
<!-- Specify the app entry point class. -->
<entry-point class='org.developerworks.digg.client.DiggApp'/>
<!-- Specify the application specific style sheet. -->
<stylesheet src='DiggApp.css' />
</module>
|
This is mostly just the file that is generated by the GWT applicationCreator script, but you have added two lines to it. Those are the two lines after the 'Other module inherits' comment in Listing 9. All you have done is add the two GWT libraries: HTTP and JSON. Now the application is ready to be compiled into JavaScript and be deployed.
It is common to see GWT code as part of a Java Web application, so it gets combined into a WAR file. In this case you are only using the client-side parts of GWT—in other words, just HTML, CSS, and JavaScript. This lets you use GWT independent of server technologies. All you need to do is compile the Java to JavaScript, take the results, and combine it with any Web application.
When you created the GWT application (using GWT's applicationCreator script), a compiler script was created for you. It is an executable, so it can be invoked simply, as shown in Listing 10.
Listing 10. Compiling GWT application
$ ./DiggApp-compile
Compiling module org.developerworks.digg.DiggApp
2008-11-08 19:56:14.962 java[1300:c1b]
[Java CocoaComponent compatibility mode]: Enabled
2008-11-08 19:56:14.963 java[1300:c1b] [Java CocoaComponent compatibility mode]:
Setting timeout for SWT to 0.100000
Compilation succeeded
Linking compilation into ./www/org.developerworks.digg.DiggApp
|
Now you just copy this folder (the one called org.developerworks.digg.DiggApp) to the Web application. Now recall that GWT runs as JavaScript, and so any calls a GWT application makes to remote servers is limited by the same-origin policy. The simplest thing to do is deploy the GWT application as part of the Grails application. To do this, simply copy it to the web-app folder in the Grails application, as shown in Figure 1.
Figure 1. GWT application deployed to Grails
Now you can run the new search application by opening it in a browser. Using the Grails conventions, the URL will be http://<root>/digg/org.developerworks.digg.DiggApp/DiggApp.html. It should look something like Figure 2.
Figure 2. The search application
Figure 2 shows the application with only the search form being displayed. Enter either a tag or a category (or both) to perform a search. The result is shown in Figure 3.
Figure 3. The search application with search results
So there is the application! This is a pretty typical GWT application using its core components. Those components provide a thin wrapper on top of standard HTML. This makes them fast and lightweight, but a little ... underwhelming, at least when compared to a lot of JavaScript-based widget kits (or what you get with Flex). Luckily you have some options, namely Ext GWT.
The Ext JS library is a very popular JavaScript library for creating applications. It has numerous rich widgets, as well as utility methods for common JavaScript tasks. It has also been translated into Java for use with the Google Web Toolkit under the name of Ext GWT. First you need to configure the application to use Ext GWT.
Setup with Ext GWT is pretty simple. First, you need the Ext GWT JAR (gxt,jar, part of the Ext GWT download) to be on your classpath. For the application, you created a lib directory and copied gxt.jar to it. You now need to add it to the compiler's classpath, so modify the DiggApp-compile script by adding './lib/gxt.jar' to the classpath there, as shown in Listing 11.
Listing 11. Modified DiggApp-compile script
#!/bin/sh APPDIR=`dirname $0`; java -XstartOnFirstThread -Xmx256M -cp "$APPDIR/src:$APPDIR/bin: /Users/michael/lib/gwt-mac-1.5.3/gwt-user.jar: /Users/michael/lib/gwt-mac-1.5.3/gwt-dev-mac.jar:./lib/gxt.jar" com.google.gwt.dev.GWTCompiler -out "$APPDIR/www" "$@" org.developerworks.digg.DiggApp; |
Next, you need to make slight modifications to the base HTML page that GWT generates. This can be found in the /public directory. The modified version is shown in Listing 12.
Listing 12. Modified DiggApp.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<!-- -->
<!-- Any title is fine -->
<!-- -->
<title>DiggApp</title>
<!-- -->
<!-- This script loads your compiled module. -->
<!-- If you add any GWT meta tags, they must -->
<!-- be added before this line. -->
<!-- -->
<script type="text/javascript" language="javascript"
src="org.developerworks.digg.DiggApp.nocache.js"></script>
<link rel="stylesheet" type="text/css" href="css/ext-all.css" />
</head>
<!-- -->
<!-- The body can have arbitrary html, or -->
<!-- you can leave the body empty if you want -->
<!-- to create a completely dynamic UI. -->
<!-- -->
<body>
<!-- OPTIONAL: include this if you want history support -->
<iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1'
style="position:absolute;width:0;height:0;border:0"></iframe>
</body>
</html>
|
Two modifications have been made. First, you changed the DOCTYPE to HTML 3.2, as this is what Ext expects. Next, you added the CSS stylesheet that Ext needs: "css/ext-all.css". Note, you do not need to actually copy this CSS file anywhere; it will be created by the compiler when it sees that the application uses the Ext GWT module. That brings us to the last modification: You need to add the Ext GWT module to the module definition. This is shown in Listing 13.
Listing 13. Module definition with GXT support
<module>
<!-- Inherit the core Web Toolkit stuff. -->
<inherits name='com.google.gwt.user.User'/>
<!-- Inherit the default GWT style sheet. You can change -->
<!-- the theme of your GWT application by uncommenting -->
<!-- any one of the following lines. -->
<inherits name='com.google.gwt.user.theme.standard.Standard'/>
<!-- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/> -->
<!-- <inherits name='com.google.gwt.user.theme.dark.Dark'/> -->
<!-- Other module inherits -->
<inherits name="com.google.gwt.http.HTTP" />
<inherits name="com.google.gwt.json.JSON" />
<inherits name='com.extjs.gxt.ui.GXT'/>
<!-- Specify the app entry point class. -->
<entry-point class='org.developerworks.digg.client.DiggApp'/>
<!-- Specify the application specific style sheet. -->
<stylesheet src='DiggApp.css' />
</module>
|
If
you compare Listing 13 to Listing 9, there is only one difference. The module
now inherits from the com.extjs.gxt.ui.GXT module.
This means that Ext GWT is configured, and you can use Ext's
widgets.
Ext includes a Grid widget that is much richer than the GWT table. It allows for many of the same things that the DataGrid in Flex allows for, like sortable and resizable columns. You will use this as a basis for a new search results display widget, as shown in Listing 14.
Listing 14. The StoryGrid widget
public class StoryGrid extends LayoutContainer {
public StoryGrid(List<Story> stories){
this.setLayout(new FlowLayout(10));
this.setSize(750, 300);
ListStore<BaseModelData> store = this.buildDataModel(stories);
Grid<BaseModelData> grid =
new Grid<BaseModelData>(store, createColumnModel());
grid.setBorders(true);
add(grid);
}
private ColumnModel createColumnModel(){
List<ColumnConfig> configs = new ArrayList<ColumnConfig>();
ColumnConfig column = new ColumnConfig();
column.setId("titleLink");
column.setHeader("Story");
column.setWidth(200);
configs.add(column);
column = new ColumnConfig();
column.setId("category");
column.setHeader("Category");
column.setWidth(100);
configs.add(column);
column = new ColumnConfig();
column.setId("description");
column.setHeader("Description");
column.setWidth(400);
configs.add(column);
return new ColumnModel(configs);
}
private ListStore<BaseModelData> buildDataModel(List<Story> stories){
ListStore<BaseModelData> data = new ListStore<BaseModelData>();
for (Story story : stories){
BaseModelData model = new BaseModelData(story.properties());
data.add(model);
}
return data;
}
}
|
A
Grid in Ext needs two basic things: column definitions and a data store. The
createColumnModel method creates the column
definitions for the Grid. It is a list of ColumnConfig objects. The key thing about ColumnConfig is its ID property. This tells the Grid the name of
the property of the data model to use. The data model is created by the buildDataModel method. You use the GXT class BaseModelData. This class just wraps around a
Java Map. In this case you used the properties()
method from the Story class. Take a look at
that in Listing
15.
Listing 15.
Story.properties method
public class Story {
public Map<String,Object> properties(){
Map<String,Object> props = new HashMap<String,Object>();
props.put("id",id);
props.put("link", link);
props.put("title", title);
props.put("description",description);
props.put("tags",tags);
props.put("category",category);
props.put("votesFor",votesFor);
props.put("votesAgainst",votesAgainst);
props.put("titleLink", getTitleLink().getHTML());
return props;
}
}
|
Now
if you go back to the ColumnConfig objects created
in the createColumnModel method in Listing 14, you
see that when you set ColumnConfig's ID to
"titleLink", then that string was used as a key into the Map created in
Listing 15. Now you can simply swap out the SearchTable for the StoryGrid back in
Listing 5, recompile, and deploy. The application now looks a little different,
as shown in Figure 4.
Figure 4. Improved search application
From here you could further enhance the UI. For instance, you could add more columns to the Grid. You could customize what columns can be used for sorting, which ones can be resized, and which ones provide access to the customization menu. You could even replace the search form widgets with more widgets from Ext. The sky's the limit.
In the first article in this series, you created an SOA back end using Grails and a UI using Flex. This time you took that back end and added a new Web service to it. Once again Grails and Groovy made this very easy to do. You created a new search application using GWT and saw how GWT can work with any Web service using its HTTP and JSON libraries. Finally, you looked at Ext GWT as a way to add richer UI widgets to GWT. Now you can create feature-rich applications in pure Java that is compiled into JavaScript that runs completely on the client.
| Description | Name | Size | Download method |
|---|---|---|---|
| Example Grails Application | digg.zip | 1101KB | HTTP |
| Example GWT app source | digg-GWT.zip | 2990KB | HTTP |
Information about download methods
Learn
- Part 1 of this
series explores how to build a Web application using Grails and Flex.
- The application created in this article can
be deployed to any application server. See an example of this in the
developerWorks article "Apache Geronimo on Grails" (Michael Galpin, developerWorks, July 2008).
-
"Mastering Grails: Build your first Grails application" (Scott Davis,
developerWorks, January 2008) gives an introduction to creating Grails
applications.
-
"Mastering Grails: GORM: Funny name, serious technology" (Scott Davis,
developerWorks, February 2008) shows you the power of GORM in action.
-
"RESTful Web services and their Ajax-based clients" (Shailesh K. Mishra,
developerWorks, July 2007) explores combining RESTful Web services, like
the ones developer here, with Ajax applications.
-
"Introducing Project Zero: RESTful applications in an SOA" (Roland
Barcia and Steve Ims, developerWorks, January 2008) shows how REST-based
services fit in perfectly in an SOA system.
-
"Exploring
the Google Web Toolkit" (Philip McArthy, developerWorks, June 2006)
gives
an introduction to the Google Web Toolkit.
-
"Build an
Ajax-enabled application using the Google Web Toolkit and Apache
Geronimo" (Michael Galpin, developerWorks, May 2007) explores using the
Google Web Toolkit as part of a Java Web application.
-
"Build an Ajax
application using Google Web Toolkit, Apache Derby, and Eclipse" (Noel
Rappin, developerWorks, December 2006) explains how GWT is designed to work with Eclipse.
Learn how to use Eclipse with it.
-
"Integrate XForms with the Google Web Toolkit" (Michael Galpin,
developerWorks, September 2007) explains that GWT can integrate with any JavaScript using
its JSNI interface. See some of the possibilities this opens up in this
developerWorks article.
- Read the Ext GWT reference documentation and
samples on the GXT site.
-
"Build
Ajax applications with Ext JS" (John Fronckowiak, developerWorks, July
2008) shows that Ext GWT is of course based on Ext JS. Learn all about Ext JS.
-
"Mastering Grails: Changing the view with Groovy Server Pages" (Scott
Davis, developerWorks, March 2008): Don't like the UI of this app? Learn all
the ways to change it.
-
"Practically Groovy: Reduce code noise with Groovy" (Scott Hickey,
developerWorks, September 2006): Are you a fan of the succinctness that Groovy
provides? Learn all about its concise syntax.
- Groovy is another promising language that
compiles to Java bytecode. Read about creating XML with it in the
developerWorks article, "Practically Groovy: Mark it up with Groovy Builders" (Andrew Glover,
developerWorks, April 2005).
-
Grails.org: The best place for Grails
information is the project's site.
-
The Grails manual: Every Grails
developer's best friend.
- Take a look at a
series of rough benchmarks to see how Grails has significant advantages
over Rails by advantages being on top of Java, Hibernate, and Spring.
- Deploying an application on Geronimo is
easy, but there is a lot that goes on. Learn all about it in the
developerWorks article, "Understand Geronimo's deployment architecture" (Hemapani Srinath
Perera, developerWorks, August 2005).
-
"Remotely deploy Web applications on Apache Geronimo" (Michael Galpin,
developerWorks, May 2006) shows how to deploy your Grails applications to
remote instances of Geronimo.
- Learn about other alternative languages
running on the JVM in the developerWorks article, "Invoke dynamic languages dynamically, Part 1: Introducing the Java
scripting API" (Tom McQueeney, developerWorks, September 2007).
-
"Build an Ajax-enabled application using the Google Web Toolkit and Apache
Geronimo" (Michael Galpin, developerWorks, May 2007) shows how Geronimo
can be used with the Google Web Toolkit.
- To listen to interesting interviews and
discussions for software developers, check out developerWorks podcasts.
- Stay current with
developerWorks technical events and webcasts.
Get products and technologies
-
Grails: This article uses Grails
Version 1.0.3.
- Get the Google Web Toolkit: This
article uses Version 1.5.3.
-
Java SDK: This
article uses Java SE 1.6_05.
- Download IBM product evaluation
versions, and get your hands on application development tools and
middleware products from DB2, Lotus, Rational, Tivoli, and WebSphere.
Discuss
- Participate in developerWorks blogs and
get involved in the developerWorks community.
Comments (Undergoing maintenance)






