Typically, web applications developed in GWT imply the use of a Java application server (eg. Tomcat, Jetty, Glashfish, etc).
You can use a normal HTTP server like Apache or IIS to develop web services with server-side language of your choice.
Let us first of all how to create an ajax request into GWT client:
String url = "http://localhost/myservice.php";
RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, url);
String postString = "par1=val1&par2=val2&par3=val3";
builder.setHeader("Content-Type", "application/x-www-form-urlencoded");
try {
builder.sendRequest(postString, new RequestCallback(){
@Override
public void onResponseReceived(Request request, Response response) {
if( 200==response.getStatusCode() ) {
String jsonString = response.getText();
MyObject myObj = parseJson(jsonString);
}
}
@Override
public void onError(Request request, Throwable exception) {}
});
}catch(RequestException e) {}
The "jsonString"
string must then be deserialized into an object.
If we use the json format is sufficient to create a JSNI method to execute the "eval" javascript statement; if the format is in XML
you can use a XMLparser.
native final MyObject parseJson(String json) /*-{
return eval("("+ json +")");
}-*/;
where MyObject is a class which must extend "JavaScriptObject" and with methods needed to read the properties.
class MyObject extends JavaScriptObject {
protected MyObject(){}
public final native String getMyProperty1()/*-{ return this.myProperty1 }-*/;
public final native int getMyProperty2()/*-{ return this.myProperty2 }-*/;
}
json string returned from server should be something like this:
{ 'myProperty1':'hello', 'myProperty2':2534 }
You can create in php like this:
<?php
$response = array('myProperty1'=>'hello', 'myProperty2'=>2534);
echo json_encode($response);
?>
It's good practise to handle js error:
try {
MyObject myObj = parseJson(jsonString);
}catch(JavaScriptException ex) {
// handle parsing error
}
Said this, It remains, therefore, the problem of running service in "debug mode" on Eclipse (this modality uses a java application server listening on port 8888).
The problem is due to "cross domain" browser restrictions .
On the client side, you are at "127.0.0.1:8888" (ip address debug mode) and you need to call a service at "127.0.0.1:80" (apache http server). The browser will not permit this.
To solve this problem, I created a GWT service (RemoteServiceServlet) called DebugService
.
This service acts like a proxy and then simly submit your request by sending cookies (eg. php session id) and all HTTP POST parameters .
Obviously you have to implement a code that checks when to use the debugging service, or contact the service php. For example, in this way:
if( GWT.getHostPageBaseURL().indexOf("http://127.0.0.1:8888") >= 0 ){
//call debug service (RemoteService)
DebugServiceAsync debugService = GWT.create(DebugService.class);
debugService.callJsonService("par1=val1&par2=val2","http://localhost/myService.php", new AsyncCallback<String>(){
public void onSuccess(String jsonString){
//TODO handle json string
}
public void onError(Throwable caught) {
// TODO handle error
}
});
}else {
//call php service (RequestBuilder)
RequestBuilder builder = new RequestBuilder(
RequestBuilder.POST, "http://localhost/myService.php");
// ....
}
DebugServiceImpl.java (pkg server)
package it.gheryd.testgwtphp.server;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLConnection;
import javax.servlet.http.Cookie;
import it.gheryd.testgwtphp.client.jsonServices.DebugService;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
public class DebugServiceImpl extends RemoteServiceServlet implements DebugService {
@Override
public String callJsonService(String postParams, String url) {
HttpURLConnection conn = null;
InputStream is = null;
try {
URL phpService = new URL( url );
conn = (HttpURLConnection )phpService.openConnection();
conn.addRequestProperty("Content-Type", "application/x-www-form-urlencoded");
boolean useCookies = false;
if(this.getThreadLocalRequest().getHeader("Cookie")!=null) {
conn.addRequestProperty("Cookie", this.getThreadLocalRequest().getHeader("Cookie"));
useCookies = true;
}
if(postParams!=null && postParams.length()>0 ) {
conn.setDoOutput(true);
OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream());
out.write(postParams);
out.close();
}
if (conn.getResponseCode() >= 400) {
is = conn.getInputStream();
} else {
is = conn.getErrorStream();
}
is = conn.getInputStream();
BufferedReader in = new BufferedReader( new InputStreamReader(is) );
String jsonResponse = "";
String inputLine;
while ((inputLine = in.readLine()) != null) jsonResponse += inputLine;
in.close();
if(useCookies){
String headerName=null;
for (int i=1; (headerName = conn.getHeaderFieldKey(i))!=null; i++) {
if (headerName.equals("Set-Cookie")) {
String cookie = conn.getHeaderField(i);
cookie = cookie.substring(0, cookie.indexOf(";"));
String cookieName = cookie.substring(0, cookie.indexOf("="));
String cookieValue = cookie.substring(cookie.indexOf("=") + 1, cookie.length());
this.getThreadLocalResponse().addCookie(new Cookie(cookieName, cookieValue));
}
}
}
return jsonResponse;
} catch(Exception ex) {
this.log("errore in debug\n"+"url: "+url+"\nconn:"+conn+" \nis:"+is, ex);
return "{type:\"error\",value:{message:\""+ex.getClass().getSimpleName()+" - "+ex.getMessage()+"\"}}";
}
}
}
DebugService.java (pkg client)
package it.gheryd.testgwtphp.client.jsonServices;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
@RemoteServiceRelativePath("debugService")
public interface DebugService extends RemoteService {
public String callJsonService( String postParams, String url );
}
DebugServiceAsync .java
package it.gheryd.testgwtphp.client.jsonServices;
import com.google.gwt.user.client.rpc.AsyncCallback;
public interface DebugServiceAsync {
void callJsonService(String postParams, String url, AsyncCallback callback);
}
The service JAVA must be declared in the file / WEB-INF/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">
<!-- Servlets -->
<!-- Default page to serve -->
<welcome-file-list>
<welcome-file>TestGwtPhp.html</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>debugService</servlet-name>
<servlet-class>it.gheryd.testgwtphp.server.DebugServiceImpl</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>debugService</servlet-name>
<url-pattern>/testgwtphp/debugService</url-pattern>
</servlet-mapping>
</web-app>
At this point you know what you need to make an application with which you can make debugging of Eclipse (or NetBeans).
If you want to see a practical example, after you find a project that I hope will be clarified.
In this project I created a series of classes under the package "jsonService" that automate the ajax request process.
The Eclipse project has this structure:
As you can see, the files ". php" are in the "war" folder of the project.
In my example I added in the configuration file httpd.conf
Apache alias:
httpd.conf
Alias /testgwtphp "C:/Users/gheryd/workspace/TestGwtPhp/war/"
<Directory "C:/Users/gheryd/workspace/TestGwtPhp/war/">
Options Indexes FollowSymLinks MultiViews
AllowOverride all
Order Deny,Allow
Deny from all
Allow from 127.0.0.1
</Directory>
for which the services in PHP HTTP Server can be accessed at "http://localhost/testgwtphp/".
In the package it.gheryd.testgwtphp.client.jsonServices
find the classes that use it for calls to PHP services.
The class JsonService is used to create a ajax request .
This class automatically decides whether to make the request directly to the service or whether to call the PHP DebugService (RemoteServiceServlet). It also takes care to deserialize the JSON string bean object.
The code below is highlighted in yellow the url used in debug
mode.
JsonService.java
package it.gheryd.testgwtphp.client.jsonServices;
import java.util.Map;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptException;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.user.client.rpc.AsyncCallback;
public class JsonService {
private static final String TYPE_ERROR = "error";
private static final String BASE_SERVICE_URL = GWT.getHostPageBaseURL();
private static final String BASE_DEBUG_SERVICEURL = "http://127.0.1.1/testgwtphp/";
public static boolean DEBUG = false;
private DebugServiceAsync debugService =null;
private String serviceUrl = null;
public JsonService(String serviceUrl) {
this.serviceUrl = serviceUrl;
}
public <T extends JavaScriptObject> void request(String jsonParams, JsonServiceCallback<T> callback) {
try {
String postString = createPostString(jsonParams);
if(DEBUG)
startDebugRequest(postString, callback );
else
startRequest(postString, callback );
}catch(JavaScriptException ex) {
callback.onError("javascript exception: "+ex.getMessage()+" jsonParams: "+jsonParams);
}
}
public <T extends JavaScriptObject> void request(Map<String,String> paramsMap, JsonServiceCallback<T> callback) {
String postString = createPostString(paramsMap);
if(DEBUG)
startDebugRequest(postString, callback );
else
startRequest(postString, callback);
}
private <T extends JavaScriptObject> void startDebugRequest(String postString, final JsonServiceCallback<T> callback) {
if(debugService==null) {
debugService = GWT.create(DebugService.class);
}
String url = BASE_DEBUG_SERVICEURL+serviceUrl;
debugService.callJsonService(postString, url, new AsyncCallback<String>() {
@Override
public void onSuccess(String result) {
handleSuccess(result, callback);
}
@Override
public void onFailure(Throwable caught) {
callback.onError(caught.getMessage());
}
});
}
private <T extends JavaScriptObject> void startRequest(String postString, final JsonServiceCallback<T> callback) {
String url = BASE_SERVICE_URL+serviceUrl;
RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, url);
builder.setHeader("Content-Type", "application/x-www-form-urlencoded");
try{
builder.sendRequest(postString, new RequestCallback(){
@Override
public void onResponseReceived(Request request, Response response) {
if( 200==response.getStatusCode() ) {
handleSuccess(response.getText(), callback);
}else {
callback.onError( "error status:"+response.getStatusCode() );
}
}
@Override
public void onError(Request request, Throwable exception) {
callback.onError(exception.getMessage());
}
});
}catch(RequestException e){
callback.onError(e.getMessage());
}
}
private <T extends JavaScriptObject> void handleSuccess(String json, JsonServiceCallback<T> callback) {
ResponseBean responseBean = null;
try {
responseBean = deserializeResponse( json );
}catch(JavaScriptException exception) {
callback.onError("javascript exception: "+exception.getMessage()+" json->"+json);
return;
}
if( TYPE_ERROR.equals(responseBean.getType()) ) {
try {
ErrorBean errorBean = (ErrorBean)responseBean.getValue();
callback.onError( errorBean.getMessage() );
}catch(JavaScriptException exception){
callback.onError("error object deserialization:"+exception.getMessage()+" json: "+json );
}
return;
}
callback.onSuccess( (T)responseBean.getValue() );
}
private final native ResponseBean deserializeResponse(String json) /*-{
var response = eval( "("+json+")" );
if( !response.type ) throw "type is undefined";
if( response.value===undefined ) throw "value is undefined";
if( typeof(response.value) == "string" ) {
response.value = {"str":response.value};
}
return response;
}-*/;
private final native String createPostString(String json)/*-{
if(!json) return null;
var obj = eval( "("+json+")" );
var s = "";
for(var k in obj) {
s += (s.length==0?"":"&") + k + "=" + obj[k];
}
return s;
}-*/;
private String createPostString(Map<String,String> paramsMap) {
if(paramsMap==null || paramsMap.size()==0 ) return null;
String s = "";
for(String k: paramsMap.keySet()) {
s += (s.length()==0?"":"&") + k + "=" + paramsMap.get(k);
}
return s;
}
}
The interface JsonServiceCallback is similar to that of GWT AsyncCallback
; in a similar manner should implement this interface to manage the response in case of success or failure.
JsonServiceCallback.java
package it.gheryd.testgwtphp.client.jsonServices;
import com.google.gwt.core.client.JavaScriptObject;
public interface JsonServiceCallback <T extends JavaScriptObject> {
public void onSuccess(T result);
public void onError(String error);
}
The classes ResponseBean
and ErrorBean
are used internally to JsonService
, and define the formats of the data that the service must return PHP (explained in more detail below)
ResponseBean.java
package it.gheryd.testgwtphp.client.jsonServices;
import com.google.gwt.core.client.JavaScriptObject;
class ResponseBean extends JavaScriptObject {
protected ResponseBean(){}
public final native String getType() /*-{
return this.type;
}-*/;
public final native JavaScriptObject getValue() /*-{
return this.value;
}-*/;
}
ErrorBean.java
package it.gheryd.testgwtphp.client.jsonServices;
import com.google.gwt.core.client.JavaScriptObject;
public class ErrorBean extends JavaScriptObject {
protected ErrorBean() {}
public final native String getMessage() /*-{
return this.message;
}-*/;
}
The class JsString
is used if the data value returned by the service PHP
is a simple string.
JsString.java
package it.gheryd.testgwtphp.client.jsonServices;
import com.google.gwt.core.client.JavaScriptObject;
public class JsString extends JavaScriptObject{
protected JsString() {}
public final native String getString()/*-{
return this.str;
}-*/;
}
For completeness also follows the content of the GWT module configuration file:
TestGwtPhp.gwt.xml
<?xml version="1.0" encoding="UTF-8"?>
<module rename-to='testgwtphp'>
<!-- module rename-to='testgwtphp' -->
<!-- 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.clean.Clean'/>
<!-- <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 -->
<!-- Specify the app entry point class. -->
<entry-point class='it.gheryd.testgwtphp.client.TestGwtPhp'/>
<!-- Specify the paths for translatable code -->
<source path='client'/>
<source path='shared'/>
</module>
Now that we have defined the basic classes for asynchronous communication client / server, including debug mode, we explain how to use this system.
To make an ajax call is sufficient to instantiate the class and pass the url on JsonService of our service:
JsonService myService = new JsonService("MyService.php");
so the request will be made by calling the "request":
myService.request(null, new JsonServiceCallback<MyBean>(){
public void onSuccess(MyBean result) {
//TODO response ok
}
public void onError(String error) {
//TODO error
}
});
The bean that will return me the service is to extend the class "com.google.gwt.core.client.JavaScriptObject"
public class MyBean extends JavaScriptObject {
protected MyBean() {}
public final native String getMyProperty()/*-{
return this.myPropery;
}-*/;
}
PHP side of the service will return a JSON string in this format:
{
type: "MyBean",
value: {myProperty: "Hello World!" }
}
or in case of error
{
type: "error",
value: {message: "This is a error"}
}
To pass the POST data in PHP Service must create a hash map:
Map<String,String> postParams = new HashMap<String,String>();
postParams.put("myparam", "myvalue");
myService.request(postParams, new JsonServiceCallback<MyBean>(){
public void onSuccess(MyBean result) {
//TODO response ok
}
public void onError(String error) {
//TODO error
}
});
or alternatively you can pass a json string:
String postParams = "{'myparam':'myvalue'}";
The service php must be something like this:
<?php
if( !isset($_POST["myparam"]) ) {
echo json_encode( array("type"=>"error", "value"=>array("message"=>"missing post param") ));
return;
}
echo json_encode( array("type"=>"MyBean", "value"=>array("myProperty"=>" gwt-php test ") ));
?>
As already explained, the service can return a simple string. In this case you must use the class JsString
:
myService.request(postParams, new JsonServiceCallback<JsString>(){
public void onSuccess(JsString result) {
String s = result.getStr();
//TODO response ok
}
public void onError(String error) {
//TODO error
}
});
The json response will then simply:
{
type: "MyBean",
value: "Hello World!"
}
Sample implementation
In applying the test that I developed using the PHP session to store the names entered by the user via the GUI.
The look and feel of this page:
In the class JsonService
was configured as the base url for use in debugging PHP services:
private static final String BASE_DEBUG_SERVICEURL = "http://127.0.1.1/testgwtphp/";
To enable debug mode should lead to true
the static variable class JsonService
, you can set it to automatically:
JsonService.DEBUG = GWT.getHostPageBaseURL().indexOf("http://127.0.0.1:8888") >= 0;
At this point we see the GWT module of the test.
TestGwtPhp.java
package it.gheryd.testgwtphp.client;
import java.util.HashMap;
import java.util.Map;
import it.gheryd.testgwtphp.client.jsonServices.JsString;
import it.gheryd.testgwtphp.client.jsonServices.JsonService;
import it.gheryd.testgwtphp.client.jsonServices.JsonServiceCallback;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
/**
* Entry point classes define <code>onModuleLoad()</code>.
*/
public class TestGwtPhp implements EntryPoint {
private JsonService addUserService = new JsonService("AddUserService.php");
private JsonService getUsersService = new JsonService("GetUsersService.php");
private JsonService deleteUsersService = new JsonService("DeleteUsers.php");
public void onModuleLoad() {
final SimplePanel resultPanel = new SimplePanel();
resultPanel.getElement().getStyle().setBackgroundColor("#EEE");
resultPanel.getElement().getStyle().setProperty("border", "1px solid black");
resultPanel.getElement().getStyle().setProperty("padding", "10px");
VerticalPanel vp = new VerticalPanel();
vp.setSpacing(10);
final TextBox nameTB = new TextBox();
final TextBox surnameTB = new TextBox();
final CheckBox debugCB = new CheckBox("debug");
HorizontalPanel hp = new HorizontalPanel();
hp.add( new HTML("name: ") );
hp.add( nameTB );
hp.add( new HTML("surname: ") );
hp.add( surnameTB );
vp.add(hp);
Button addUserBtn = new Button("add user");
Button getUsersBtn = new Button("get users");
Button deleteUsersBtn = new Button("delete users");
hp = new HorizontalPanel();
hp.add(addUserBtn);
hp.add(getUsersBtn);
hp.add(deleteUsersBtn);
hp.add(debugCB);
vp.add(hp);
vp.add(resultPanel);
final VerticalPanel usersPanel = new VerticalPanel();
vp.add(usersPanel);
debugCB.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
JsonService.DEBUG = debugCB.getValue();
resultPanel.setWidget( new HTML("debug mode: "+JsonService.DEBUG) );
}
});
debugCB.setValue( GWT.getHostPageBaseURL().indexOf("http://127.0.0.1:8888") >= 0 , true );
JsonService.DEBUG = debugCB.getValue();
addUserBtn.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
Map<String,String> params = new HashMap<String,String>();
params.put("name", nameTB.getText());
params.put("surname", surnameTB.getText());
addUserService.request(params, new JsonServiceCallback<JsString>(){
@Override
public void onSuccess(JsString result) {
resultPanel.setWidget( new HTML("Result: "+result.getString()) );
}
@Override
public void onError(String error) {
resultPanel.setWidget( new HTML("<span style=\"color:red;\">ERROR!</span> "+error) );
}
});
}
});
getUsersBtn.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
usersPanel.clear();
String params = null;
getUsersService.request(params, new JsonServiceCallback<JsArray<UserBean>>(){
@Override
public void onSuccess(JsArray<UserBean> result) {
resultPanel.setWidget( new HTML("users: "+result.length()) );
for(int i=0; i<result.length(); i++) {
UserBean ub = result.get(i);
usersPanel.add( new HTML( ub.getName()+" "+ub.getSurname() ) );
}
}
@Override
public void onError(String error) {
resultPanel.setWidget( new HTML("<span style=\"color:red;\">ERROR!</span> "+error) );
}
});
}
});
deleteUsersBtn.addClickHandler(new ClickHandler(){
@Override
public void onClick(ClickEvent event) {
String params = null;
deleteUsersService.request(params, new JsonServiceCallback<JsString>() {
@Override
public void onSuccess(JsString result) {
resultPanel.setWidget( new HTML("Result: "+result.getString()) );
}
@Override
public void onError(String error) {
resultPanel.setWidget( new HTML("<span style=\"color:red;\">ERROR!</span> "+error) );
}
});
}
});
RootPanel.get().add(vp);
}
}
It 's interesting use of the class JsArray
(see gwt api) used to switch the list of objects UserBean
. Not necessarily have to use a bean object that you define, you can pass any object that extends JavaScriptObject
.
The bean that I used is as follows:
UserBean.java
package it.gheryd.testgwtphp.client;
import com.google.gwt.core.client.JavaScriptObject;
public class UserBean extends JavaScriptObject {
protected UserBean() {}
public final native String getName()/*-{
return this.name;
}-*/;
public final native String getSurname()/*-{
return this.surname;
}-*/;
}
And finally the PHP services used:
AddUserService.php
<?php
if( !isset($_POST) ) {
echo json_encode( array("type"=>"error", "value"=>array("message"=>"missing post params")) );
return;
}else if( empty($_POST["name"]) ) {
echo json_encode( array("type"=>"error", "value"=>array("message"=>"missing post param 'name'")) );
return;
}else if( empty($_POST["surname"]) ) {
echo json_encode( array("type"=>"error", "value"=>array("message"=>"missing post param 'surname'")) );
return;
}
$name = $_POST["name"];
$surname = $_POST["surname"];
session_start();
$response = array();
if( !isset($_SESSION["list"]) ) $_SESSION["list"] = array();
$_SESSION["list"][] = array("name"=>$name, "surname"=>$surname);
$response["type"] = "result";
$response["value"] = "ok";
echo json_encode($response);
?>
DeleteUsers.php
<?php
session_start();
unset($_SESSION["list"]);
echo json_encode( array("type"=>"result", "value"=>"ok") );
?>
GetUsersService.php
<?php
session_start();
if( !isset($_SESSION["list"]) ) {
echo json_encode( array("type"=>"error", "value"=>array("message"=>"no list in session")) );
return;
};
$response = array("type"=>"userList" );
$response["value"] = $_SESSION["list"];
echo json_encode($response);
?>
We can now run our application from Eclipse:
while to run the application directly from Apache, you must first compile it:
Once completed you can access the url "127.0.0.1/testgwtphp/TestGwtPhp.html".
Remember to start Apache!
Deepening
I have not focused on the operation of the class JsonService
but if the look is not very complex. It's definitely improved in several respects.
Error Handling
For example, error handling is dealt with very easily if not trivial, but in a real application would be more practical to treat the error as if it were an exception. In this case the interface ErrorBean
could be enriched by other properties such as the name of an exception report to the client.
For example, if the session expired the service may respond with an error like this:
{
type: "error",
value: {message:"session expired", exception:"SessionExpiredException"}
}
Then create a class exception on the client package:
public class SessionExpiredException extends Exception {
public SessionExpiredException (String message) {
super(message);
}
}
In this case the method onError
interface JsonServiceCallback
this will be the exception rather than the message string.
package it.gheryd.testgwtphp.client.jsonServices;
import com.google.gwt.core.client.JavaScriptObject;
public interface JsonServiceCallback {
public void onSuccess(T result);
public void onError(Exception Ex);
}
To verify the type of exception would be enough to control implementation JsonServiceCallback
:
myService.request(null, new JsonServiceCallback<MyBean>(){
public void onSuccess(MyBean result) {
//TODO response ok
}
public void onError(Exception ex) {
if(ex instanceof SessionExpired) {
//TODO session expired
}else {
//Window.alert(ex.getMessage());
}
}
});
In the class JsonService
must be passed the instance of an exception:
if( "SessionExpiredException".equals(errorBean.getException()) )
callback.onError( new SessionExpiredException(errorBean.getMessage()) );
Obviously you must add the property "exception" in the class ErrorBean
:
package it.gheryd.testgwtphp.client.jsonServices;
import com.google.gwt.core.client.JavaScriptObject;
public class ErrorBean extends JavaScriptObject {
protected ErrorBean() {}
public final native String getMessage() /*-{
return this.message;
}-*/;
public final native String getException() /*-{
return this.exception;
}-*/;
}
The methods JSNI
When you create a class that extends JavascriptObject, care must be taken to the return type in JSNI methods. In the following sample code is provided, for example, the type of return value:
public class MyObject extends JavaScriptObject{
protected MyObject(){}
public final native int getIntNumber()/*-{
return isNaN(this.intNumber)?0:this.intNumber;
}-*/;
public final native String getSomeString()/*-{
return typeof(this.someString)=="string"? this.someString : "";
}-*/;
}
This avoids errors in the code difficult to detect. Within JSNI methods you can put a notification to identify the causes of the error:
public class MyObject extends JavaScriptObject{
protected MyObject(){}
public final native int getIntNumber()/*-{
if(isNaN(this.intNumber)){
console.log("***error***");
console.log(this);
this.intNumber = 0;
}
return this.intNumber;
}-*/;
}
Hash Map
If a service needs to return a map of objects (an associative array in PHP), client-side GWT we can implement a class for the purpose:
public class JHashMap <V extends JavaScriptObject> extends JavaScriptObject{
protected JHashMap(){}
public final native JsArrayString keys()/*-{
return this.keys;
}-*/;
public final native V get(String key)/*-{
return this.values[key];
}-*/;
}
PHP side of the map should be created as:
<?php
$objectList = array(
"obj1" => array("property1"=>"value1","property2"=>"value2"),
"obj2" => array("property1"=>"value1","property2"=>"value2"),
"obj3" => array("property1"=>"value1","property2"=>"value2")
);
$map = array(
"keys" => array_keys($objectList),
"values" => $objectList
);
echo json_encode( array("type"=>"JHashMap", "value"=>$map ) );
?>
From GWT client we manage well the answer:
myservice.request(null, new JsonServiceCallback<
JHashMap<MyBean> >(){
@Override
public void onSuccess(JHashMap<
MyBean> result) {
JsArrayString keys = result.keys();
for(int i=0; i<keys.length(); i++ ){
MyBean myBean = result.get(keys.get(i));
// ....
}
}
@Override
public void onError(String error) {
//handle error
}
});