GWT and Java Script Native Interface (JSNI) calls

package com.mattiz.jsni.client;

import java.util.Date;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.JsDate;
import com.google.gwt.dev.jjs.ast.JStringLiteral;
import com.google.gwt.dev.js.ast.JsStringLiteral;
import com.google.gwt.dev.js.ast.JsVars.JsVar;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Window;
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.TextBox;

public class HelloJsni implements EntryPoint {

	@Override
	public void onModuleLoad() {
		Label label = new Label(
				"Enter date in any of these formats: MM/dd/yyyy, M/dd/yyyy, MM/d/yyyy M/d/yyyy, "
						+ "MM.dd.yyyy, M.dd.yyyy, MM.d.yyyy,M.d.yyyy, yyyy/MM/dd, yyyy/MM/d, yyyy/M/dd, yyyy/M/d, "
						+ "yyyy.MM.dd, yyyy.MM.d, yyyy.M.dd, yyyy.M.d, MM/dd/yy, M/d/yy,MM/d/yy M/dd/yy, "
						+ "MM.dd.yy M.d.yy MM.d.yy M.dd.yy ,MMddyyyy");
		label.setStyleName("gwt-Label"); // Change css to use this
		HorizontalPanel hp = new HorizontalPanel();
		RootPanel.get().add(label);
		final TextBox tb = new TextBox();
		tb.addBlurHandler(new BlurHandler() {
			@Override
			public void onBlur(BlurEvent event) {
				String stringJavaDate = tb.getValue();
				String formattedDate = loadDateValidator(stringJavaDate);
				tb.setValue(formattedDate);
			}
		});
		hp.add(tb);
		RootPanel.get().add(label);
		RootPanel.get().add(hp);
	}

	private static native String loadDateValidator(String stringJsVar) /*-{
		return $wnd.convertDateFormat(stringJsVar);//calls method in js file.
	}-*/;
}

Note the js call using JSNI (Java Script Native Interface) in the native function private static native String loadDateValidator().

Source code hierarchy

dateformatter.js (this has been replicated in other posts on this blog)

// Format the Date entered in the any below format to MM/dd/yyyy format:
// Add more date formats here to increase date format range accepted
function convertDateFormat(dateValue) {
    if (!isValidJSDate(dateValue)) {
        alert("Unexpected Date Format");
    }   
    var dateArray = new Array();
    dateArray[0] = "MM/dd/yyyy";
    dateArray[1] = "M/dd/yyyy";
    dateArray[2] = "MM/d/yyyy";
    dateArray[3] = "M/d/yyyy";
 
    dateArray[4] = "yyyy/MM/dd";
    dateArray[5] = "yyyy/MM/d";
    dateArray[6] = "yyyy/M/dd";
    dateArray[7] = "yyyy/M/d";
 
    dateArray[8] = "MM/dd/yy";
    dateArray[9] = "M/d/yy";
    dateArray[10] = "MM/d/yy";
    dateArray[11] = "M/dd/yy";
 
    dateArray[12] = "MM.dd.yyyy";
    dateArray[13] = "M.dd.yyyy";
    dateArray[14] = "MM.d.yyyy";
    dateArray[15] = "M.d.yyyy";
 
    dateArray[16] = "yyyy.MM.dd";
    dateArray[17] = "yyyy.MM.d";
    dateArray[18] = "yyyy.M.dd";
    dateArray[19] = "yyyy.M.d";
 
    dateArray[20] = "MM.dd.yy";
    dateArray[21] = "M.d.yy";
    dateArray[22] = "MM.d.yy";
    dateArray[23] = "M.dd.yy";
 
    dateArray[24] = "MMddyyyy";
 
    if (dateValue != "") {
        for ( var i = 0; i < dateArray.length; ++i) {
            var date = getDateFromFormat(dateValue, dateArray[i]);
            if (date != 0) {
                dateValue = formatDate(new Date(date));
            }
        }
    }
    return dateValue;
}
// to validate the below formats for frontend
// Add more date formats here to increase date format range accepted
function isValidJSDate(dateValue) {
    var flag = false;
    if (dateValue == "") {
        return true;
    }
    var dateArray = new Array();
    dateArray[0] = "MM/dd/yyyy";
    dateArray[1] = "M/dd/yyyy";
    dateArray[2] = "MM/d/yyyy";
    dateArray[3] = "M/d/yyyy";
 
    dateArray[4] = "yyyy/MM/dd";
    dateArray[5] = "yyyy/MM/d";
    dateArray[6] = "yyyy/M/dd";
    dateArray[7] = "yyyy/M/d";
 
    dateArray[8] = "MM/dd/yy";
    dateArray[9] = "M/d/yy";
    dateArray[10] = "MM/d/yy";
    dateArray[11] = "M/dd/yy";
 
    dateArray[12] = "MM.dd.yyyy";
    dateArray[13] = "M.dd.yyyy";
    dateArray[14] = "MM.d.yyyy";
    dateArray[15] = "M.d.yyyy";
 
    dateArray[16] = "yyyy.MM.dd";
    dateArray[17] = "yyyy.MM.d";
    dateArray[18] = "yyyy.M.dd";
    dateArray[19] = "yyyy.M.d";
 
    dateArray[20] = "MM.dd.yy";
    dateArray[21] = "M.d.yy";
    dateArray[22] = "MM.d.yy";
    dateArray[23] = "M.dd.yy";
 
    dateArray[24] = "MMddyyyy";
 
    for ( var i = 0; i < dateArray.length; ++i) {
        var date = getDateFromFormat(dateValue, dateArray[i]);
        if (date != 0) {
            flag = true;
        }
    }
    return flag;
}
 
// Format the Date object to MM/dd/yyyy format:
function formatDate(date) {
    var dateString = "";
    if (date) {
        var d = date.getDate();
        var day = (d < 10) ? '0' + d : d;
        var m = date.getMonth() + 1;
        var month = (m < 10) ? '0' + m : m;
        var yy = date.getFullYear();
        yy += '';
        switch (yy.length) {
        case 0:
            yy = '0000';
            break;
        case 1:
            yy = '000' + yy;
            break;
        case 2:
            yy = '00' + yy;
            break;
        case 3:
            yy = '0' + yy;
            break;
        }
        dateString = month + "/" + day + "/" + yy;
    }
    return dateString;
}
 
// Utility function. returns date in time format.
var MONTH_NAMES = new Array('January', 'February', 'March', 'April', 'May',
        'June', 'July', 'August', 'September', 'October', 'November',
        'December', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug',
        'Sep', 'Oct', 'Nov', 'Dec');
var DAY_NAMES = new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday',
        'Thursday', 'Friday', 'Saturday', 'Sun', 'Mon', 'Tue', 'Wed', 'Thu',
        'Fri', 'Sat');
function LZ(x) {
    return (x < 0 || x > 9 ? "" : "0") + x
}
 
function getDateFromFormat(val, format) {
    val = val + "";
    format = format + "";
    var i_val = 0;
    var i_format = 0;
    var c = "";
    var token = "";
    var token2 = "";
    var x, y;
    var now = new Date();
    var year = now.getYear();
    var month = now.getMonth() + 1;
    var date = 1;
    var hh = now.getHours();
    var mm = now.getMinutes();
    var ss = now.getSeconds();
    var ampm = "";
    while (i_format < format.length) {
        // Get next token from format string
        c = format.charAt(i_format);
        token = "";
        while ((format.charAt(i_format) == c) && (i_format < format.length)) {
            token += format.charAt(i_format++);
        }
        // Extract contents of value based on format token
        if (token == "yyyy" || token == "yy" || token == "y") {
            if (token == "yyyy") {
                x = 4;
                y = 4;
            }
            if (token == "yy") {
                x = 2;
                y = 2;
            }
            if (token == "y") {
                x = 2;
                y = 4;
            }
            year = _getInt(val, i_val, x, y);
            if (year == null) {
                return 0;
            }
            i_val += year.length;
            if (year.length == 2) {
                if (year > 70) {
                    year = 1900 + (year - 0);
                } else {
                    year = 2000 + (year - 0);
                }
            }
        } else if (token == "MMM" || token == "NNN") {
            month = 0;
            for ( var i = 0; i < MONTH_NAMES.length; i++) {
                var month_name = MONTH_NAMES[i];
                if (val.substring(i_val, i_val + month_name.length)
                        .toLowerCase() == month_name.toLowerCase()) {
                    if (token == "MMM" || (token == "NNN" && i > 11)) {
                        month = i + 1;
                        if (month > 12) {
                            month -= 12;
                        }
                        i_val += month_name.length;
                        break;
                    }
                }
            }
            if ((month < 1) || (month > 12)) {
                return 0;
            }
        } else if (token == "EE" || token == "E") {
            for ( var i = 0; i < DAY_NAMES.length; i++) {
                var day_name = DAY_NAMES[i];
                if (val.substring(i_val, i_val + day_name.length).toLowerCase() == day_name
                        .toLowerCase()) {
                    i_val += day_name.length;
                    break;
                }
            }
        } else if (token == "MM" || token == "M") {
            month = _getInt(val, i_val, token.length, 2);
            if (month == null || (month < 1) || (month > 12)) {
                return 0;
            }
            i_val += month.length;
        } else if (token == "dd" || token == "d") {
            date = _getInt(val, i_val, token.length, 2);
            if (date == null || (date < 1) || (date > 31)) {
                return 0;
            }
            i_val += date.length;
        } else if (token == "hh" || token == "h") {
            hh = _getInt(val, i_val, token.length, 2);
            if (hh == null || (hh < 1) || (hh > 12)) {
                return 0;
            }
            i_val += hh.length;
        } else if (token == "HH" || token == "H") {
            hh = _getInt(val, i_val, token.length, 2);
            if (hh == null || (hh < 0) || (hh > 23)) {
                return 0;
            }
            i_val += hh.length;
        } else if (token == "KK" || token == "K") {
            hh = _getInt(val, i_val, token.length, 2);
            if (hh == null || (hh < 0) || (hh > 11)) {
                return 0;
            }
            i_val += hh.length;
        } else if (token == "kk" || token == "k") {
            hh = _getInt(val, i_val, token.length, 2);
            if (hh == null || (hh < 1) || (hh > 24)) {
                return 0;
            }
            i_val += hh.length;
            hh--;
        } else if (token == "mm" || token == "m") {
            mm = _getInt(val, i_val, token.length, 2);
            if (mm == null || (mm < 0) || (mm > 59)) {
                return 0;
            }
            i_val += mm.length;
        } else if (token == "ss" || token == "s") {
            ss = _getInt(val, i_val, token.length, 2);
            if (ss == null || (ss < 0) || (ss > 59)) {
                return 0;
            }
            i_val += ss.length;
        } else if (token == "a") {
            if (val.substring(i_val, i_val + 2).toLowerCase() == "am") {
                ampm = "AM";
            } else if (val.substring(i_val, i_val + 2).toLowerCase() == "pm") {
                ampm = "PM";
            } else {
                return 0;
            }
            i_val += 2;
        } else {
            if (val.substring(i_val, i_val + token.length) != token) {
                return 0;
            } else {
                i_val += token.length;
            }
        }
    }
    // If there are any trailing characters left in the value, it doesn't match
    if (i_val != val.length) {
        return 0;
    }
    // Is date valid for month?
    if (month == 2) {
        // Check for leap year
        if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) { // leap
                                                                            // year
            if (date > 29) {
                return 0;
            }
        } else {
            if (date > 28) {
                return 0;
            }
        }
    }
    if ((month == 4) || (month == 6) || (month == 9) || (month == 11)) {
        if (date > 30) {
            return 0;
        }
    }
    // Correct hours value
    if (hh < 12 && ampm == "PM") {
        hh = hh - 0 + 12;
    } else if (hh > 11 && ampm == "AM") {
        hh -= 12;
    }
    var newdate = new Date(year, month - 1, date, hh, mm, ss);
    return newdate.getTime();
}
// Utility functions for parsing in getDateFromFormat()
function _isInteger(val) {
    var digits = "1234567890";
    for ( var i = 0; i < val.length; i++) {
        if (digits.indexOf(val.charAt(i)) == -1) {
            return false;
        }
    }
    return true;
}
function _getInt(str, i, minlength, maxlength) {
    for ( var x = maxlength; x >= minlength; x--) {
        var token = str.substring(i, i + x);
        if (token.length < minlength) {
            return null;
        }
        if (_isInteger(token)) {
            return token;
        }
    }
    return null;
}

JSNITest.gwt.xml
Note how the js file has been included in the gwt.xml file and not in the default html file.

<?xml version="1.0" encoding="UTF-8"?>
<module rename-to='JSNITest'>
  <!-- Inherit the core Web Toolkit stuff.                        -->
  <inherits name='com.google.gwt.user.User'/>
 
  <inherits name='com.google.gwt.user.theme.standard.Standard'/>
 
  <!-- Specify the app entry point class.                         -->
  <entry-point class='com.mattiz.jsni.client.HelloJsni'/>
  <script src="./dateFormatter.js">
    <![CDATA[
      if ($wnd.$) {
        return true;
      }
      else {
        return false;
      }
    ]]>
  </script>
</module>

JSNITest.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <link type="text/css" rel="stylesheet" href="JSNITest.css">
    <title>My First GWT application</title>
    <script type="text/javascript" language="javascript" src="jsnitest/jsnitest.nocache.js"></script>
  </head>
  <body>
    <!-- OPTIONAL: include this if you want history support -->
    <a href=""></a>
  </body>
</html>

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>
    <!-- Default page to serve -->
    <welcome-file-list>
        <welcome-file>JSNITest.html</welcome-file>
    </welcome-file-list>
</web-app>

Snapshot below:

Sample Calculator Application using GWT With RPC Calls

The sample application written adds two numbers or subtracts one number from another. An RPC call
is made from client to server and the actual addition/ subtraction is done on the server side.
The UI of the application looks something like this:

There is a single entry point on the java side and it implements the EntryPoint interface that
must implement the onModuleLoad() method. The war file looks for an xml where this entry point
class is specified usually with the name “appname”.gwt.xml
In the sample application the entry point class is CalculatorServer.java and it is specified as
the entry point class in SampleProject.gwt.xml. It also contains most of the java code that is
rendered into html components, javascript and css. The javascript version of the endpoint class is
generated by gwt and in our case it is called sampleproject.nocache.js. We need not modify this
file.
If we use domain objects that are shared between client and server it is mandatory that they
implement the serializable interface.
However the SampleProject application does not use any such domain objects.
The domain object gets passed between browser and server serialized and deserialized by the framework.
SampleService.java is the interface that extends RemoteService and it is the serverside interface
exposing server side methods. The object is passed in some serialized form, maybe as xml or xml
representation of a java object using xmlhttprequest and xmlhttpresponse. In the sample
application the method exposed on the server side is public String getResult(String num1, String
mathAction, String num2);
It expects as arguments the two numbers on which the mathematical
operation is to be performed upon and the actual mathematical operator.
GWT uses asynchronous communication therefore you also need to create the asynchronous version of
this interface. The name of this interface must be the interface name concatenated with “Async”.
which is what the framework needs. In the sample application the following methods is exposed in
SampleServiceAsync.java:
void getResult(String num1, String mathAction, String num3, AsyncCallback callback);

SampleServiceAsync interface is implemented by framework and our classes don’t implement it.
SampleService.java has this code
@RemoteServiceRelativePath(“userService”)
public interface SampleService extends RemoteService {

“userService” is the alias by which UserService is exposed to the client.
It is specified in web.xml as a servlet as follows:

  <servlet>
     <servlet-name>userServlet</servlet-name>
     <servlet-class>com.mattiz.sample.server.SampleServiceImpl</servlet-class>
  </servlet>  
  <servlet-mapping>
     <servlet-name>userServlet</servlet-name>
     <url-pattern>/sampleproject/userService</url-pattern>
  </servlet-mapping>

Note that userServlet points to the implementation of SampleService which is SampleServiceImpl
which extends RemoteServiceServlet and implements SampleService.
In the SampleServiceImpl the business implementation on the server side is implemented. In the
sample example the addition or subtraction on the parameters passed from the front end is
performed.
The entrypoint class, CalculatorServer.java calls the server side method in the following code:
SampleServiceAsync service = (SampleServiceAsync) GWT
.create(SampleService.class);
ServiceDefTarget serviceDef = (ServiceDefTarget) service;
serviceDef.setServiceEntryPoint(GWT.getModuleBaseURL() + “userService”);
SampleCallback sampleCallback = new SampleCallback(tb);
service.getResult(num1, mathAction, num2, sampleCallback);

Note that the alias of SampleServiceImpl which is sampleproject/userService, is invoked within
CalculatorServer class. The servlet is mapped to a path ending with /userService and the client
side code(javascript) may invoke it. The client code calls this service hidden to us which is how
ajax basically works.
SampleCallBack is the class which sends/ recieves data from the server. SampleCallBack is called
when the service URL is executed by AJAX. The Async service returns this type of callback object
populated with the result of the server side application.
Each service will have its own callback class. The callback class for our server side method is
SampleCallBack implements AsyncCallback
In the sample application it implements two methods:
onFailure(Throwable caught) {
onSuccess(String result) {

onFailure is called on an unsuccessful call to the server. On a successful call to the server side
method, onSuccess() is called. Inside this method the value recieved from the server side call is
populated in the textbox from the parameter of the onSuccess() method.
private TextBox tb.setValue(result);
SampleCallBack is executed when response comes back from server. On failure it pops a window alert
and on success it populates the textfield with data.
SampleCallback sampleCallback = new SampleCallback(textbox);
in the entrypoint class calls the constructor of the SampleCalback class and passes on the texbox
variable to the Callback class.
CalculatorServer entry point class has several buttons, for numbers 0-9 and “+”, “-“.. On clicking
any of these buttons an anonymous inner class is called with the following code
button.addClickHandler(new ClickHandler() {
It is within this class that the data from the UI is transferred to server side through an RPC
call and data is received in a similar way from the callback class and is displayed on the UI.
The response from URL is got in SampleCallback class service.getUserList(myUserCallback);
Note this line in the entry point class:
serviceDef.setServiceEntryPoint(GWT.getModuleBaseURL()
+ “userService”);

This is where the AJAX call is made to the URL on button click and SampleServiceAsync is invoked.
The response from URL is got in SampleCallback class and the textfied contained in SampleCallback
is populated with data.
The response from URL is got in SampleCallback class:
service.getResult(num1, mathAction, num2, sampleCallback);
That invokes the service and results are populated in the callback object.
getResult is the method exposed by the service so service.getResult(..) populates SampleCallBack
and SampleCallBack populates the textfield:
private TextBox tb.setValue(result);
A reference of Samplecallback is passed to service.getResult and it gets populated with server
side data.
The important thing with AJAX is that it enhances user experience so you can invoke service on
tab-out, tab-in etc don’t have to wait until button click.
Finally you would need to modify the system generated html file, SampleProject.html with the following lines, to successfully generate the GWT based UI.

<link type="text/css" rel="stylesheet" href="SampleProject.css">
<title>Web Application Starter Project</title>
<script type="text/javascript" language="javascript"
    src="sampleproject/sampleproject.nocache.js"></script>
</head>

Note: In order to run GWT on eclipse you may install android dev toolkit (ADT)plugin and then google web toolkit (GWT) plugin that are available on the google site.

Project Heirarchy below:

package com.mattiz.sample.client.entrypoint;

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.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.mattiz.sample.client.model.CalculationCounter;
import com.mattiz.sample.client.service.SampleCallback;
import com.mattiz.sample.client.service.SampleService;
import com.mattiz.sample.client.service.SampleServiceAsync;

public class CalculatorServer implements EntryPoint {

	private TextBox tb = new TextBox();

	public void onModuleLoad() {
		Button button0 = new Button("0");
		button0.setSize("75", "75");
		Button button1 = new Button("1");
		button1.setSize("75", "75");
		Button button2 = new Button("2");
		button2.setSize("75", "75");
		Button button3 = new Button("3");
		button3.setSize("75", "75");
		Button button4 = new Button("4");
		button4.setSize("75", "75");
		Button button5 = new Button("5");
		button5.setSize("75", "75");
		Button button6 = new Button("6");
		button6.setSize("75", "75");
		Button button7 = new Button("7");
		button7.setSize("75", "75");
		Button button8 = new Button("8");
		button8.setSize("75", "75");
		Button button9 = new Button("9");
		button9.setSize("75", "75");
		Button button10 = new Button("+");
		button10.setSize("75", "75");
		Button button11 = new Button("-");
		button11.setSize("75", "75");

		button0.addStyleName("calc-btn");
		button1.addStyleName("calc-btn");
		button2.addStyleName("calc-btn");
		button3.addStyleName("calc-btn");
		button4.addStyleName("calc-btn");
		button5.addStyleName("calc-btn");
		button6.addStyleName("calc-btn");
		button7.addStyleName("calc-btn");
		button8.addStyleName("calc-btn");
		button9.addStyleName("calc-btn");

		HorizontalPanel hPanel1 = new HorizontalPanel();
		hPanel1.setWidth("30%");
		hPanel1.setHorizontalAlignment(HorizontalPanel.ALIGN_LEFT);
		hPanel1.add(button1);
		hPanel1.add(button2);
		hPanel1.add(button3);

		HorizontalPanel hPanel2 = new HorizontalPanel();
		hPanel2.setWidth("30%");
		hPanel2.setHorizontalAlignment(HorizontalPanel.ALIGN_LEFT);
		hPanel2.add(button4);
		hPanel2.add(button5);
		hPanel2.add(button6);

		HorizontalPanel hPanel3 = new HorizontalPanel();
		hPanel3.setWidth("30%");
		hPanel3.setHorizontalAlignment(HorizontalPanel.ALIGN_LEFT);
		hPanel3.add(button7);
		hPanel3.add(button8);
		hPanel3.add(button9);

		HorizontalPanel hPanel4 = new HorizontalPanel();
		hPanel4.setWidth("30%");
		hPanel4.setHorizontalAlignment(HorizontalPanel.ALIGN_LEFT);
		hPanel4.add(button0);
		hPanel4.add(button10);
		hPanel4.add(button11);
		HorizontalPanel hPanel5 = new HorizontalPanel();
		tb.setSize("60", "25");
		hPanel5.add(tb);

		RootPanel.get().add(hPanel1);
		RootPanel.get().add(hPanel2);
		RootPanel.get().add(hPanel3);
		RootPanel.get().add(hPanel4);
		RootPanel.get().add(hPanel5);

		final CalculationCounter CalculationCounter = new CalculationCounter();
		button0.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				if (CalculationCounter.getNum1() == null) {
					CalculationCounter.setNum1("0");
				} else if (CalculationCounter.getNum2() == null) {
					CalculationCounter.setNum2("0");
					getValueFromService(CalculationCounter.getNum1(),
							CalculationCounter.getMathOperation(), CalculationCounter.getNum2());
					CalculationCounter.reset();
				}
			}
		});
		button1.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				if (CalculationCounter.getNum1() == null) {
					CalculationCounter.setNum1("1");
				} else if (CalculationCounter.getNum2() == null) {
					CalculationCounter.setNum2("1");
					getValueFromService(CalculationCounter.getNum1(),
							CalculationCounter.getMathOperation(), CalculationCounter.getNum2());
					CalculationCounter.reset();
				}
			}
		});
		button2.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				if (CalculationCounter.getNum1() == null) {
					CalculationCounter.setNum1("2");
				} else if (CalculationCounter.getNum2() == null) {
					CalculationCounter.setNum2("2");
					getValueFromService(CalculationCounter.getNum1(),
							CalculationCounter.getMathOperation(), CalculationCounter.getNum2());
					CalculationCounter.reset();
				}
			}
		});
		button3.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				if (CalculationCounter.getNum1() == null) {
					CalculationCounter.setNum1("3");
				} else if (CalculationCounter.getNum2() == null) {
					CalculationCounter.setNum2("3");
					getValueFromService(CalculationCounter.getNum1(),
							CalculationCounter.getMathOperation(), CalculationCounter.getNum2());
					CalculationCounter.reset();
				}
			}
		});
		button4.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				if (CalculationCounter.getNum1() == null) {
					CalculationCounter.setNum1("4");
				} else if (CalculationCounter.getNum2() == null) {
					CalculationCounter.setNum2("4");
					getValueFromService(CalculationCounter.getNum1(),
							CalculationCounter.getMathOperation(), CalculationCounter.getNum2());
					CalculationCounter.reset();
				}
			}
		});
		button5.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				if (CalculationCounter.getNum1() == null) {
					CalculationCounter.setNum1("5");
				} else if (CalculationCounter.getNum2() == null) {
					CalculationCounter.setNum2("5");
					getValueFromService(CalculationCounter.getNum1(),
							CalculationCounter.getMathOperation(), CalculationCounter.getNum2());
					CalculationCounter.reset();
				}
			}
		});
		button6.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				if (CalculationCounter.getNum1() == null) {
					CalculationCounter.setNum1("6");
				} else if (CalculationCounter.getNum2() == null) {
					CalculationCounter.setNum2("6");
					getValueFromService(CalculationCounter.getNum1(),
							CalculationCounter.getMathOperation(), CalculationCounter.getNum2());
					CalculationCounter.reset();
				}
			}
		});
		button7.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				if (CalculationCounter.getNum1() == null) {
					CalculationCounter.setNum1("7");
				} else if (CalculationCounter.getNum2() == null) {
					CalculationCounter.setNum2("7");
					getValueFromService(CalculationCounter.getNum1(),
							CalculationCounter.getMathOperation(), CalculationCounter.getNum2());
					CalculationCounter.reset();
				}
			}
		});
		button8.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				if (CalculationCounter.getNum1() == null) {
					CalculationCounter.setNum1("8");
				} else if (CalculationCounter.getNum2() == null) {
					CalculationCounter.setNum2("8");
					getValueFromService(CalculationCounter.getNum1(),
							CalculationCounter.getMathOperation(), CalculationCounter.getNum2());
					CalculationCounter.reset();
				}
			}
		});
		button9.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				if (CalculationCounter.getNum1() == null) {
					CalculationCounter.setNum1("9");
				} else if (CalculationCounter.getNum2() == null) {
					CalculationCounter.setNum2("9");
					getValueFromService(CalculationCounter.getNum1(),
							CalculationCounter.getMathOperation(), CalculationCounter.getNum2());
					CalculationCounter.reset();
				}
			}
		});
		button10.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				CalculationCounter.setMathOperation("+");
			}
		});
		button11.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
					CalculationCounter.setMathOperation("-");
			}
		});
	}

	private void getValueFromService(String num1, String mathAction, String num2) {
		SampleServiceAsync service = (SampleServiceAsync) GWT
				.create(SampleService.class);
		ServiceDefTarget serviceDef = (ServiceDefTarget) service;
		serviceDef.setServiceEntryPoint(GWT.getModuleBaseURL() + "userService");
		SampleCallback sampleCallback = new SampleCallback(tb);
		service.getResult(num1, mathAction, num2, sampleCallback);
	}
}
package com.mattiz.sample.client.service;

import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;


//
@RemoteServiceRelativePath("userService")
public interface SampleService extends RemoteService {

	public String getResult(String num1, String mathAction, String num2);
}

package com.mattiz.sample.server;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.mattiz.sample.client.service.SampleService;

public class SampleServiceImpl extends RemoteServiceServlet implements
		SampleService {

	private static final long serialVersionUID = 1L;

	public String getResult(String num1, String mathAction, String num2) {
		int numberOne = Integer.parseInt(num1);
		int numberTwo = Integer.parseInt(num2);
		if (mathAction.equals("+")) {
			return (numberOne + numberTwo) + "";
		} else if (mathAction.equals("-")) {
			return (numberOne - numberTwo) + "";
		} else
			throw new RuntimeException("INVALID ENTRY");
	}

}

package com.mattiz.sample.client.service;

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

public interface SampleServiceAsync {

	void getResult(String num1, String mathAction, String num3, AsyncCallback<String> callback);
}

package com.mattiz.sample.client.service;

import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.TextBox;


public class SampleCallback implements AsyncCallback<String> {
	private TextBox tb;

	public SampleCallback(TextBox tb) {
		this.tb = tb;
	}

	public void onFailure(Throwable caught) {
		Window.alert(caught.getMessage());
	}

	public void onSuccess(String result) {
		tb.setValue(result);
	}

}

package com.mattiz.sample.client.model;

import java.io.Serializable;

public class CalculationCounter implements Serializable {
	private  final long serialVersionUID = 5962982131382782218L;

	private  String num1;
	private  String num2;
	private  String mathOperation;
	private  int counter;

	public  String getNum1() {
		return num1;
	}

	public  void setNum1(String num1) {
		this.num1 = num1;
	}

	public  String getNum2() {
		return num2;
	}

	public  void setNum2(String num2) {
		this.num2 = num2;
	}

	public  String getMathOperation() {
		return mathOperation;
	}

	public  void setMathOperation(String mathOperation) {
		this.mathOperation = mathOperation;
	}

	public  int getCounter() {
		return counter;
	}

	public  void setCounter(int counter) {
		this.counter = counter;
	}

	public  void reset() {
		num1 = null;
		num2 = null;
		mathOperation = null;
	}
}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee  http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"    version="2.5" xmlns="http://java.sun.com/xml/ns/javaee">
  <!-- Default page to serve -->
  <welcome-file-list>
    <welcome-file>SampleProject.html</welcome-file>
  </welcome-file-list>
   
  <servlet>
    <servlet-name>userServlet</servlet-name>
    <servlet-class>com.mattiz.sample.server.SampleServiceImpl</servlet-class>
  </servlet>
   
  <servlet-mapping>
    <servlet-name>userServlet</servlet-name>
    <url-pattern>/sampleproject/userService</url-pattern>
  </servlet-mapping>
</web-app>

SampleProject.gwt.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 1.6.4//EN" "http://google-web-toolkit.googlecode.com/svn/tags/1.6.4/distro-source/core/src/gwt-module.dtd">
<module rename-to='SampleProject'>
    <inherits name='com.google.gwt.user.User' />
    <inherits name='com.google.gwt.user.theme.standard.Standard' />
 
    <!-- Specify the app entry point class. -->
    <entry-point class='com.mattiz.sample.client.entrypoint.CalculatorServer' />
</module>

SampleProject.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link type="text/css" rel="stylesheet" href="SampleProject.css">
<title>Web Application Starter Project</title>
 
<script type="text/javascript" language="javascript"
    src="sampleproject/sampleproject.nocache.js"></script>
</head>
<body>
</body>
</html>

Add these lines to the default css

.calc-btn {
	height: 5.7em;
	margin-bottom: 5px;
	padding-bottom: 3px;
	font-size: 12px;
	font-family: arial, sans-serif;
}