GWT Tutorial: Example Application (2)

Welcome to GWT Tutorial series where we will explore how to create a GWT application. This is the second part of the tutorial and it is about RPC, and Datastore from Google App Engine. To simplify the content, the Login service is ignored. That means you don't need login to add a note. The sample application finally looks like this.




Google App Engine is free, so the app is live for you to take a look. http://udnotes.appspot.com/

To create a GWT application mainly 3 parts would be hard to understand for new users:

  1. Create a client side code. This is all about simple GUI app implementation. Instead of using Swing Component, you use Component from GWT.
  2. Communicate to Server with RPC. Here is the part required more attention.
  3. User Google DataStore data. The cool part is here.

In order to complete a RPC call, three steps will be evolved. The comments in UDNotes class explains the 3 steps in detail and useful to understand the process.
1) An interface with extends RemoteService that lists the service method you need.
2) An asynchronous version of the interface above.
3) An implementation of the service which extends RemoteServiceServlet implements NoteService.

1. Create a NoteService interface under "client" package.

package com.programcreek.tutorials.client;
 
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
 
@RemoteServiceRelativePath("note")
public interface NoteService extends RemoteService {
  public void addNote(String note);
  public void removeNote(String note);
  public String[] getNotes();
}

2. Create an asynchronous version of the interface under "client" package.

package com.programcreek.tutorials.client;
 
import com.google.gwt.user.client.rpc.AsyncCallback;
 
public interface NoteServiceAsync {
	void addNote(String note, AsyncCallback<Void> callback);
	void getNotes(AsyncCallback<String[]> callback);
	void removeNote(String note, AsyncCallback<Void> callback);
}

3. Create a Note class under "server" package. This involves with how to use JDO to store data.

package com.programcreek.tutorials.server;
 
import java.util.Date;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
 
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Note {
 
	@PrimaryKey
	@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
	private Long id;
	@Persistent
	private String note;
	@Persistent
	private Date createDate;
 
	//a good way to use constructor
	public Note() {
		this.createDate = new Date();
	}
 
	public Note(String symbol) {
		this();
		this.note = symbol;
	}
 
	public Long getId() {
		return this.id;
	}
 
	public String getNote() {
		return this.note;
	}
 
	public Date getCreateDate() {
		return this.createDate;
	}
 
	public void setNote(String note) {
		this.note = note;
	}
}

4. Implement the service implementation under "server" package.

package com.programcreek.tutorials.server;
 
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
 
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
 
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.programcreek.tutorials.client.NoteService;
 
public class NoteServiceImpl extends RemoteServiceServlet implements NoteService {
 
	private static final long serialVersionUID = 1L;
 
	private static final Logger LOG = Logger.getLogger(NoteServiceImpl.class.getName());
 
	private static final PersistenceManagerFactory PMF = JDOHelper.getPersistenceManagerFactory("transactions-optional");
 
	public void addNote(String note){
		PersistenceManager pm = getPersistenceManager();
		try {
			pm.makePersistent(new Note(note));
		} finally {
			pm.close();
		}
	}
 
	public void removeNote(String note){
		PersistenceManager pm = getPersistenceManager();
		try {
			long deleteCount = 0;	
			String query = "select from " + Note.class.getName();
			List<Note> Notes = (List<Note>) pm.newQuery(query).execute();
			for (Note Note : Notes) {
				if (note.equals(Note.getNote())) {
					deleteCount++;
					pm.deletePersistent(Note);
				}
			}
			if (deleteCount != 1) {
				LOG.log(Level.WARNING, "removeNote deleted " + deleteCount
						+ " Notes");
			}
		} finally {
			pm.close();
		}
	}
 
	public String[] getNotes() {
 
		PersistenceManager pm = getPersistenceManager();
		List<String> symbols = new ArrayList<String>();
 
		try {
			String query = "select from " + Note.class.getName();
			List<Note> Notes = (List<Note>) pm.newQuery(query).execute();
 
			for (Note Note : Notes) {
				symbols.add(Note.getNote());
			}
		} finally {
			pm.close();
		}
 
		String[] ret = new String[symbols.size()];
 
		int i = 0;
		for (String s : symbols) {
			ret[i] = s;
			i++;
		}
 
		return ret;
	}
 
	private PersistenceManager getPersistenceManager() {
		return PMF.getPersistenceManager();
	}
}

5. Edit the web application deployment descriptor (/war/WEB-INF/web.xml).

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">
 
<web-app>
 
 <!-- Servlets -->  
  <servlet>
    <servlet-name>noteService</servlet-name>
    <servlet-class>com.programcreek.tutorials.server.NoteServiceImpl</servlet-class>
  </servlet>
 
 
  <servlet-mapping>
    <servlet-name>noteService</servlet-name>
    <url-pattern>/udnotes/note</url-pattern>
  </servlet-mapping>
 
  <!-- Default page to serve -->
  <welcome-file-list>
    <welcome-file>UDNotes.html</welcome-file>
  </welcome-file-list>
 
</web-app>

6. Edit UDNotes.java under "client" package.

 
package com.programcreek.tutorials.client;
 
import java.util.ArrayList;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.VerticalPanel;
 
/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class UDNotes implements EntryPoint {
 
	private VerticalPanel mainPanel = new VerticalPanel();
	private FlexTable notesFlexTable = new FlexTable();
	private HorizontalPanel addPanel = new HorizontalPanel();
	private TextArea newSymbolTextBox = new TextArea();
 
	private Button addNoteButton = new Button("Add");
	private Label lastUpdatedLabel = new Label();
	private ArrayList<String> NotesNames = new ArrayList<String>();
 
	private VerticalPanel loginPanel = new VerticalPanel();
	private Label loginLabel = new Label(
			"Please sign in to your Google Account to access the NoteWatcher application.");
 
	// (1) Create the client proxy. Note that although you are creating the
	// service interface properly, you cast the result to the asynchronous
	// version of the interface. The cast is always safe because the
	// generated proxy implements the asynchronous interface automatically.
	private final NoteServiceAsync NoteService = GWT.create(NoteService.class);
 
	public void onModuleLoad() {
 
		// Create table for Note data.
		notesFlexTable.setText(0, 0, "Note");
 
		// set button's style
		addNoteButton.addStyleName("addButton");
 
		// Assemble Add Note panel.
		addPanel.add(newSymbolTextBox);
		addPanel.add(addNoteButton);
 
		// Assemble Main panel.
		mainPanel.add(notesFlexTable);
		mainPanel.add(addPanel);
		mainPanel.add(lastUpdatedLabel);
 
		// Associate the Main panel with the HTML host page.
		RootPanel.get().add(mainPanel);
 
		// Move cursor focus to the input box.
		newSymbolTextBox.setWidth("300px");
		newSymbolTextBox.setFocus(true);
 
		// Listen for mouse events on the Add button.
		addNoteButton.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				addNote();
 
			}
		});
 
		// Listen for keyboard events in the input box.
		newSymbolTextBox.addKeyPressHandler(new KeyPressHandler() {
			public void onKeyPress(KeyPressEvent event) {
				if (event.getCharCode() == KeyCodes.KEY_ENTER) {
					addNote();
				}
			}
		});
 
		//load notes initially.
		loadNotes();
	}
 
	private void addNote() {
		final String note = newSymbolTextBox.getText().trim();
		newSymbolTextBox.setFocus(true);
		newSymbolTextBox.setText("");
 
		// Don't add the Note if it's already in the table.
		if (NotesNames.contains(note))
			return;
 
		addNote(note);
	}
 
	private void addNote(final String note) {
		// (2) Create an asynchronous callback to handle the result.
		AsyncCallback callback = new AsyncCallback<Void>() {
			public void onFailure(Throwable error) {
				// do something, when fail
				Window.alert("failed");
			}
 
			public void onSuccess(Void ignore) {
				// when successful, do something, about UI
				displayNote(note);
			}
		};
 
		// (3) Make the call. Control flow will continue immediately and later
		// 'callback' will be invoked when the RPC completes.
		NoteService.addNote(note, callback);
 
	}
 
	private void displayNote(final String note) {
		// Add the Note to the table.
		int row = notesFlexTable.getRowCount();
		NotesNames.add(note);
		notesFlexTable.setText(row, 0, note);
 
		Button removeNoteButton = new Button("x");
		removeNoteButton.addStyleDependentName("remove");
		removeNoteButton.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				removeNote(note);
			}
		});
		notesFlexTable.setWidget(row, 2, removeNoteButton);
 
	}
 
	private void removeNote(final String note) {
		NoteService.removeNote(note, new AsyncCallback<Void>() {
			public void onFailure(Throwable error) {
			}
 
			public void onSuccess(Void ignore) {
				undisplayNote(note);
			}
		});
	}
 
	private void undisplayNote(String note) {
		int removedIndex = NotesNames.indexOf(note);
		NotesNames.remove(removedIndex);
		notesFlexTable.removeRow(removedIndex + 1);
 
	}
 
	private void loadNotes() {
		NoteService.getNotes(new AsyncCallback<String[]>() {
			public void onFailure(Throwable error) {
			}
 
			public void onSuccess(String[] notes) {
				displayNotes(notes);
			}
		});
 
	}
 
	private void displayNotes( String[] notes) {
		for (String note : notes) {
			displayNote(note);
		}
	}
}

7. A real application is a good way to show how it works. This example is very basic, more functions can be added, such as login service, access control, etc.

Category >> Google Web Toolkit  
If you want someone to read your code, please put the code inside <pre><code> and </code></pre> tags. For example:
<pre><code> 
String foo = "bar";
</code></pre>

  1. Nazish on 2013-11-24

    great work…. 🙂

  2. please on 2014-1-10

    i have one urgent question.
    How do I make a data that persists for every other user that access website?
    much like comment box for many websites.
    You write something, it stays on the website for everyone to see.

  3. Arturas on 2014-9-2

    I get “failed” in a dialog when clicking the add button and running this code. Could it be because I’m running it without Google Apps Engine? Unfortunately Google Apps Engine doesn’t work for me at all and I can’t test it on it…

  4. Arturas on 2014-9-2

    Seems it wasn’t because I wasn’t running it with Google Apps Engine, but because the mapped path was slightly different for me

    noteService
    /udnotes/note

    mine was supposed to be /udnotes3/note because i called my project udnotes3 I guess

Leave a comment

*