Monday, September 14, 2009

GWT on Google Appengine

The first part list the steps to create the first google app engine GWT applicaiton(Mostly copied from Google Instruction). The second part is more detail explanation.

1. Download Eclipse and install google app engine plugin

Eclipse 3.5 (Galileo)

http://dl.google.com/eclipse/plugin/3.5

2. Create applicaiton (areyouup)

File > New > Web Application Project (areyouup, com.asianwondersnet.areyouup)

(select workbench if not show up)

3. Running your Web Application locally

Right-click on your web application project and select Debug As > Web Application from the popup menu.

This action creates an Eclipse Web Application launch configuration for you and launches it. The web application launch configuration will start a server and the GWT hosted browser.

At this point, you can set breakpoints, inspect variables and modify code as you would normally expect from a Java Eclipse debugging session.

4. Deploying your Web Application

To deploy your web application, you will need to create an application from the App Engine Administration Console, at the following URL: https://appengine.google.com/. If you already have one, then you can skip this step.

Once you have an application ID, just right-click on your project, and select Google > App Engine Settings... from the context menu. Enter your application ID into the Application ID text box. Click OK.

Right-click on your project and select Google > Deploy to App Engine. In the resulting Deploy Project to Google App Engine dialog, enter your Google Account email and password.

Note: Don't worry - the plugin doesn't store your password anywhere.

Click Deploy.

Go to http://application-id.appspot.com/ to see your application.

The following is a little bit more detail. Here are the steps to add a new service. Using TestService as example.

1. Define TestService.java

package com.asianwondersnet.areyouup.client;

import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
@RemoteServiceRelativePath("test")
public interface TestService extends RemoteService{
String testServer(String name);
}

Please notice the relative path annotation above. We will use that information in web.xml

2. Define the TestServiceAsyn.java

package com.asianwondersnet.areyouup.client;

import com.google.gwt.user.client.rpc.AsyncCallback;

public interface TestServiceAsync {
void testServer(String input, AsyncCallback callback);
}

Notice the above two service interface both has testServer. But there is a little bit difference. The second one has a AsyncCallback argument which we will show it in entrypoint code.

3. Define the TestServiceImpl.java which implement the TestService interface.

package com.asianwondersnet.areyouup.server;

import com.asianwondersnet.areyouup.client.TestService;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

@SuppressWarnings("serial")
public class TestServiceImpl extends RemoteServiceServlet implements TestService{

public String testServer(String input) {
StringBuffer sb=new StringBuffer();
try {
URL url = new URL(input);
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
String line;
while ((line = reader.readLine()) != null) {
sb.append(line+"\\n");
}
reader.close();

}finally{
if (sb.length()==0){
sb.append("Please input the full URL for fetching data!");
}
}
return sb.toString();

}
}

4. Add the servlet configuration in web.xml

The name: testServlet

The class: com.asianwondersnet.areyouup.server.TestServiceImpl

Add the servlet mapping to web.xml

map /areyouup/test to testServlet

The relative path test is annotated in the TestService.java interface.

Also define the hosting page as welcome-file-list: Areyouup.html

The hosting page was generated automatically but you can change it from the web.xml file.

5. Now the entrypoing code: Areyouup.java (This was generated automatically when the application is setup on Eclipse).

package com.asianwondersnet.areyouup.client;

import com.google.gwt.core.client.EntryPoint;

public class Areyouup implements EntryPoint{

private final TestServiceAsync testService = GWT.create(TestService.class);

//The entrypoint
public void onModuleLoad() {

//Define the buttons/textfields specify style as necessary
final Button sendButton = new Button("Send");
final TextBox nameField = new TextBox();
nameField.setText("Input URL");

// We can add style names to widgets
sendButton.addStyleName("sendButton");

// Add the nameField and sendButton to the RootPanel
// Use RootPanel.get() to get the entire body element
RootPanel.get("nameFieldContainer").add(nameField);
RootPanel.get("sendButtonContainer").add(sendButton);

// Focus the cursor on the name field when the app loads
nameField.setFocus(true);
nameField.selectAll();

// Create the popup dialog box
final DialogBox dialogBox = new DialogBox();
dialogBox.setText("Remote Procedure Call");
dialogBox.setAnimationEnabled(true);
final Button closeButton = new Button("Close");
// We can set the id of a widget by accessing its Element
closeButton.getElement().setId("closeButton");
final Label textToServerLabel = new Label();
final HTML serverResponseLabel = new HTML();
VerticalPanel dialogVPanel = new VerticalPanel();
dialogVPanel.addStyleName("dialogVPanel");
dialogVPanel.add(new HTML("Sending name to the server:"));
dialogVPanel.add(textToServerLabel);
dialogVPanel.add(new HTML("Server replies:"));
dialogVPanel.add(serverResponseLabel);
dialogVPanel.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT);
dialogVPanel.add(closeButton);
dialogBox.setWidget(dialogVPanel);

// Add a handler to close the DialogBox
closeButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
dialogBox.hide();
sendButton.setEnabled(true);
sendButton.setFocus(true);
}
});

// Create a handler for the sendButton and nameField
class MyHandler implements ClickHandler, KeyUpHandler {
/**
* Fired when the user clicks on the sendButton.
*/
public void onClick(ClickEvent event) {
sendNameToServer();
}

/**
* Fired when the user types in the nameField.
*/
public void onKeyUp(KeyUpEvent event) {
if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
sendNameToServer();
}
}

/**
* Send the name from the nameField to the server and wait for a response.
*/
private void sendNameToServer() {
sendButton.setEnabled(false);
String textToServer = nameField.getText();
textToServerLabel.setText(textToServer);
serverResponseLabel.setText("");
testService.testServer(textToServer,
new AsyncCallback() {
public void onFailure(Throwable caught) {
// Show the RPC error message to the user
dialogBox
.setText("Remote Procedure Call - Failure");
serverResponseLabel
.addStyleName("serverResponseLabelError");
serverResponseLabel.setHTML(SERVER_ERROR);
dialogBox.center();
closeButton.setFocus(true);
}

public void onSuccess(String result) {
dialogBox.setText("Remote Procedure Call");
serverResponseLabel
.removeStyleName("serverResponseLabelError");
serverResponseLabel.setHTML(result);
dialogBox.center();
closeButton.setFocus(true);
}
});
}
}

// Add a handler to send the name to the server
MyHandler handler = new MyHandler();
sendButton.addClickHandler(handler);
nameField.addKeyUpHandler(handler);
}

}

The GWT has been simplified a bit from older versions. No more Service casting and the annotation also helps simplify coding.

6. Look at the Hosting page:

In the html header, link style "Areyouup.css" and java script src="areyouup/areyouup.nocache.js"
In the body define the field "nameFieldContainer" and "sendButtonContainer" refered in the code

Ok. Here is the play ground. It has the above function plus some more experimental image animation stuff. The URL:http://areyouup.appspot.com/


Thursday, September 10, 2009

Application development process for Google Appengine on Grails

Application development process for Google Appengine on Grails

This is the note I took while watching grocher 's Screen cast from the Grails 1.1.1 announcement
So all honor goes to grocher and the grails/groovy team.

1. grails create-app grails-music-store
2. Goto appengine.google.com, add new application grails-music-store . Note the app name must match
3. cd grails-music-store
4. grails uninstall-plugin hibernate
5. grails install-plugin app-engine
6. export APPENGINE_HOME=/Developer/appengine-java-sdk-1.2.0
7. grails app-engine
then goto 8080 and see the skeleton app
8. grails create-domain-class Album
JDO by default
9. grails generate-all com.music.Album
10. grails app-engine
On Windows, After kill the application, the java process is still running. I have to kill the java process manually. (currports is a nice tool for this process)
11. grails app-engine package
12. /Developer/appengine-java-sdk-1.2.0/bin/appcfg.sh update ./target/war
provide password //first time
It will fail. Fail because of version.
13. grails set-version 1 (This is not the app engine version)
14. do step 11-12 to build and deploy again
/Developer/appengine-java-sdk-1.2.0/bin/appcfg.sh update ./target/war
15. Check it out
http://grails-music-store.appspot.com/album/create (or list...)

Of course you need to install the app engine sdk first.