Validation in GWT
Client Side Validation:
DTO Class UserDto:
public class UserDto implements Serializable {
private int userId;
private String userName;
}
Remote Service interface:
public interface MyService extends RemoteService {
public String saveUser(UserDto userDto);
}
Remote Service Asynchronous interface:
public interface MyServiceAsync {
public void saveUser(UserDto userDto, AsyncCallback<String> callback);
}
The client side call will be as follows:
MyServiceAsync myService = MyService.Util.getInstance();
UserDto user = new UserDto();
user.setUserId(Integer.parseInt(userId.getText()));
user.setUserName(userName.getText());
myService.saveUser(user, new AsyncCallback<String>() {
…
So when user for example enters string in user id, a number format exception will be raised which can be caught and appropriate error messages can be displayed. For debugging purposes the exception details are displayed in FireBug and the Hosted mode server console. (see GWT documentation for the emulated java exceptions).
Server side Validation:
Invalid input types:
The serialized request sent to the RPC service is mentioned as follows:
http://host:8888/TestValidation/?485975FDB961BEAB5B5D6F881FE9205A?demo.client.MyService?saveUser?demo.client.UserDto?demo.client.UserDto/1050776168?text?1?2?3?4?1?5?6?2342?7?
The dispatcher Servlet uses the interface name and method requested to get the bean implementing the requested interface, by using reflection, it gets the method parameters
By using ServerSerializationStreamReader utility class, it de-serializes the request parameter to the actual method parameter:
ServerSerializationStreamReader.deserializeValue(parameterTypes[i]);
The ServerSerializationStreamReader class, validates the parameters for its compatibility with method parameter types, and create the method parameters. If invalid parameters are sent, a serialization exception is sent back to the client (internal server error (code: 500)
Business constraint violations:
Since GWT handles the incorrect types automatically (String instead of integer for example), the business constraints on data transfer objects should be handled by the service invocation layer (RPC Handler)
- Approach 1:
- Using third party validation party (for example Oval http://oval.sourceforge.net/) which allows defining annotations on the client DTO
- Approach 2:
- Validating the input DTO manually by the service invocation layer
In order to allow third party annotations to be recognized by the GWT Compiler, the library should be included on the class path of the GWT compiler; this ant script illustrates the idea
<path id=”classpath”>
<pathelement location=”${gwtpath}/gwt-user.jar” />
<pathelement location=”${gwtpath}/gwt-dev-windows.jar” />
<pathelement location=”${src.dir}” />
<fileset dir=”${lib.dir}”>
<include name=”**/*.jar” />
</fileset>
</path>
<target name=”compile-gwt” depends=”init”>
<java classname=”com.google.gwt.dev.GWTCompiler” fork=”true”>
<classpath refid=”classpath” />
<jvmarg value=”-Xmx256M” />
<arg value=”-out” />
<arg value=”${www.dir}” />
<arg value=”${gwt.module}” />
</java>
</target>
Springfying GWT
Google toolkit allows usage of Spring framework in the server side. The
goal of this approach is to decouple the business interface (defined in the
client side) from the web interface.
Step 1:
Create business interface created in the client package that extends
RemoteService interface
package demo.client;
public interface Test extends RemoteService {
public static final String SERVICE_URI = "RPCHandler";
public static class Util {
public static TestAsync getInstance() {
TestAsync instance = (TestAsync) GWT.create(Test.class);
ServiceDefTarget target = (ServiceDefTarget) instance;
target.setServiceEntryPoint(GWT.getModuleBaseURL() +
SERVICE_URI);
return instance;
}
}
public String sayHello(String name);
}
Step 2:
Create the asynchronous business interface (e.g. MyServiceAsycn.java).
(Note: The <interfaceName>Async naming convention is important for the GWT compiler)
package demo.client;
public interface TestAsync {
public void sayHello(String s, AsyncCallback<String> callback);
}
Step 3:
The dispatcher Servlet acts as the controller to invoke the services implementations that are defined as spring beans. The dispatcher Servlet uses
WebApplicationContextUtils.getWebApplicationContext(getServletContext()).getBean(interfaceName)
to get the requested service implementation bean. The dispatcher Servlet design follows these steps:
Create an ordinary Servlet to handle all GWT RPC calls, and then Servlet
implementation is responsible for invoking the requested service
method.
Define the service implementation as a spring bean to implement the
business interface defined at the client.
package demo.server;
@Transactional
public class HandlerService implements Test {
public String sayHello(String name) {
return "Hello " + name;
}
Use RPC.decodeRequest utility class to get the RPC request, which contains
the business interface name , method to be invoked and parameters, the
encoded request looks similar to this pattern:
http://host:8888/TestValidation/?485975FDB961BEAB5B5D6F881FE9205A?demo.client.MyService?saveUser
?demo.client.UserDto?demo.client.UserDto/1050776168?text?1?2?3?4?1?5?6?2342?7?
where,
demo.client.MyService: Interface name
saveUser: Method to be invoked
demo.client.UserDto: method parameters object type(s)
Service implementation invocation design approaches:
- The service implementation is defined as a Spring bean , with an ID
equals to the name of the interface - The service implementation is defined as a Spring bean with id equals
to the name of the interface and method (<interface
FQN>.methodname;
The RPC Handler Servlet ,invokes the method requested in the payload on
the retrieved spring bean
The RPC Handler uses the utility classes that comes with the GWT library
(RPC , RPCServletUtil …)
protected void doPost(HttpServletRequest httpRequest,HttpServletResponse httpResponse) throws ServletException,IOException {
String payload =
RPCServletUtils.readContentAsUtf8(httpRequest);
Object service = null;
String responsePayload = null;
RPCRequest rpcRequest = RPC.decodeRequest(payload);
Class clazz = rpcRequest.getMethod().getDeclaringClass();
service = getService(clazz.getName());
responsePayload = RPC.invokeAndEncodeResponse(service, rpcRequest.getMethod(), rpcRequest.getParameters(), rpcRequest.getSerializationPolicy());
boolean gzipEncode = false;
RPCServletUtils.writeResponse(getServletContext(),httpResponse,responsePayload, gzipEncode);
-
Recent
-
Links
-
Archives
- July 2009 (1)
- October 2008 (3)
-
Categories
-
RSS
Entries RSS
Comments RSS