2008/11/24
2008/11/12
GWT RPC from non-servlet-container - finally
Finally, I get to this part after the first and second rounds.
GwtRpc
A Scala module(object) GwtRpc, is created for this RPC purpose, which defines three traits of interest:
- GwtRpcSpp
- RpcService
- SppHolder
GwtRpcSpp is a simple trait extending GWT's SerializationPolicyProvider, which declares an abstract method getPolicyFileInputStream:
def getPolicyFileInputStream(moduleBaseUrl:String,
strongName:String):Option[InputStream]
moduleBaseUrl and stringName are two parameters carried by client request of GWT RPC. This method is supposed to be implemented to allow users to hook up their own logic about getting serialize policy file generated by GWT RPC.RpcService is another simple trait which is supposed to be mixed into entities who is going to be the end-implementations of GWT RPC services.trait RpcService { this:SppHolder with RemoteService =>
def call(final in: => String)(handler: PartialFunction[CallResult, Unit])={
...
}
...
}
Method call is the main entry of a RpcService implementation, which takes two arguments.- in - a Call-by-name string: the UTF-8 payload of an RPC request;
- handler - a partial function which is provided by user to match the result of a call and write result back to response
SppHolder is a container of SerializationPolicyProvider. RpcService declares it as a part of its self type annotation which sort of declares a dependency to SppHolder, only the dependency needs to be fulfilled by its implementation.Actually, this GwtRpc module is container-independent. It can be used in any container which provides IO stream as abstraction of HTTP request.
The Restlet implementation
Cause I (in this series) take more interesting in serving GWT RPC via Restlet, I would give a simple implementation for Restlet environment.Generally, Restlet allows you to serve a HTTP request via a Restlet or a Resource. As a result, two traits implementations for each of them are provided respectively.
For Restlet:
trait RpcRestlet extends Restlet with RpcService with SppHolder{this:RemoteService=>
override def handle(req:Request, resp:Response){
req.getMethod match{
case Method.POST=>
call(fromStream(req.getEntity.getStream))(writeRpcResult(resp))
case _ => // do nothing
}
}
}
For Resource:trait RpcResource extends Resource with RpcService with SppHolder{this:RemoteService=>
override def acceptRepresentation(rep: Representation){
call(fromStream(rep.getStream))(writeRpcResult(getResponse))
}
}
fromStream is a method to read payload from an InputStream. This payload is simple a UTF-8 string and will be decoded to an instance of RpcRequest by GWT's RPC.decodeRequest(String).writeRpcResult is a functional variable, which takes a Response as argument and returns a PartialFunction:val writeRpcResult: Response=>PartialFunction[CallStatus, Unit] = {r=>{
case CallSuc(msg)=>
val rep = new StringRepresentation(msg, MediaType.APPLICATION_JSON)
rep.setCharacterSet(CharacterSet.UTF_8)
rep.setDownloadable(true)
rep.setDownloadName("attachment")
r.setEntity(rep)
r.setStatus(Status.SUCCESS_OK)
case CallFail(msg)=>
r.setEntity(new StringRepresentation(msg, MediaType.TEXT_PLAIN))
r.setStatus(Status.SERVER_ERROR_INTERNAL)
}
}
To create an implementation of GWT RemoteService, just do almost same thing as in GWT but change the parent class to either one of above. For example:abstract class PingServiceImpl extends RpcRestlet with PingService with SppHolder{
def ping(msg:String)={
"Pong-> " + msg + "@" + new Date
}
}
PingServiceImpl is still abstract, because its dependency to SerializationPolicyProvider has not been fulfilled. To make thing simple, I reuse the App created in last installment as the base and make it a SerializationPolicyProvider for the whole Application scope.The implementation looks like following code, using Directory
gwtDir is a lazy way here.Finally attach ping service to the router of this application and specify the SppHolder dependency:
class App extends Application with GwtApp with GwtRpcSpp{self=>
def getPolicyFileInputStream(moduleBaseUrl:String,
strongName:String):Option[InputStream]={
val ref = new Reference(moduleBaseUrl)
val response = gwtDir.get(ref.getLastSegment+"/"+getPolicyFileName(strongName))
if(Status.SUCCESS_OK == response.getStatus)
Some(response.getEntity.getStream)
else
None
}
}
There is no need to change code of GWT client, just let ping service call endpoint http://localhost:8080/ping will done.
override def createRoot={
//...
r.attach("/ping", new PingServiceImpl{
def provider = self
})
//...
}
For complete source code of this sample, see http://edgebox.googlecode.com/svn/sandbox/gwt-restlet-demo/.
Posted by Unknown at 11:50:00 PM 0 comments
2008/11/06
Generate RequestBuilder logic using RestGWT
Besides GWT's RPC, GWT also allows user to communicate backend web services using standard HTTP calls. The latter, comparing with RPC, is much more flexible and supports arbitry payload formats.
However, coding with GWT HTTP calls (the RequestBuilder) is a redundant and repeating work. Besides worrying about preparing the payload content, developers also need to write lots of lines of code to prepare the instance of RequestBuilder itself, to worry about metadata (http headers) used for it, to care of the URL of interface and the method of the HTTP call.
This does not sound too much a big deal if the backend service is sort of traditional RPC style, in which the interface (the URL) of endpoint is usually fixed and these metadata used by HTTP calls are usually unchanged. For a RESTful style service, things might become a lit more complex, in which
- Each resource is unique identified by a URI;
- Same kind of resources' URIs conforms to same pattern;
- Metadata such as HTTP headers usually instruct how the backend service to process this request and impact the representation of the response;
Present you RestGWT, the generator for RequestBuilder logic
RestGWT is a GWT generator implementation, particular for simplifying coding work about GWT HTTP Calls, specifically, HTTP Calls to a RESTful service.RestGWT steals some concepts and nice ideas from JAX-RS, WADL and Restlet, says for making a HTTP Calls you will need
- An
@Endpointwhich specifies the uri template in the Restlet style, i.e./ci/{type}/{id}and the method used to do the call - One or more
@HttpHeaders indicates metadata, i.e@HttpHeader(name="Content-Type", value="application/ci+xml)" - Apply above two items on a method of an interface, i.e.
void getCI(String type, String id, RequestCallback callback); @UriPartcan be used to indicate how to replace url template with values specified by method parameters, so the method signature now becomesvoid getCI(@UriPart("type") String type, @UriPart("id") String id);- Finally, the interface containing the method needs extends interface
org.edgebox.gwt.rest.client.RestRemoteas a flag;
Now, to use this service, using GWT RPC style:
public interface MyService extends RestRemote{
@Endpoint(method=RestGWT.Method.Represnt, urlTemplate="/a/ci/{type}/{id}")
@HttpHeader(name="Content-Type", value="application/ci+xml")
public void getCI(@UrlPart("type") String type,
@UrlPart("id") String id, RequestCallback, callback);
}
MyService service = GWT.create(MyService.class)Is this a nice cool idea? I am not quite sure, but at least it suits my requirement pretty well. At current moment, RestGWT is still under heavy working. But you can get source code from
RequestCallback callback = //...
service.getCI("person", "111", callback)
http://edgebox.googlecode.com/svn/trunk/edgebox-restgwt for a look.
Posted by Unknown at 2:10:00 PM 1 comments
2008/10/31
GWT RPC from non-servlet-container (2)
In the second post, I would like to show-off the project structure of a simple GWT application served by pure Restlet (which means not running in a servlet container) Application as a sample and basis of further discussion.
The GWT Client
In order to deploy a GWT application in non-servlet container like Restlet, I can not package the GWT client in a war. I choose to package everything (class files, java sources, gwt compiled js and other static resources) into a single jar. It might be also friend to OSGi environment.
I use gwt-maven plugin as the build tool, which is quite handy in our case. For detail usage of gwt-maven plugin, please check the example. I made some modification in order to create this "special" jar.
<plugin>
<groupId>com.totsp.gwt</groupId>
<artifactId>maven-googlewebtoolkit2-plugin</artifactId>
<version>2.0-beta24</version>
<configuration>
...
<output>${project.build.directory}/classes/_gwt</output>
</configuration>
</plugin>
I changed the output directory of GWT resources, so that they can easily get packaged by maven jar plugin.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<classifier>gwt</classifier>
<excludes>
<exclude>_gwt/.gwt-tmp/**</exclude>
</excludes>
</configuration>
</execution>
</executions>
<configuration>
<excludes>
<exclude>_gwt/**</exclude>
</excludes>
</configuration>
</plugin>
Two jars are created, one with classifier "gwt" includes all static resources.
<build>
<resources>
<resource>
<directory>./src/main/java</directory>
</resource>
<resource>
<directory>./src/main/resources</directory>
</resource>
</resources>
...
</build>
This is a small trick to package java sources into GWT jar.
The Restlet Application
(Sorry for using Scala as the example language, but hopefully you will see how Scala can ease the development)At current moment, there is no standard way to deploy a web application using Restlet. Basically in Restlet, you expose dynamic part as a Resource, and serve static things using Directory (see example on Restlet site).
I create a GwtApp trait for serving GWT client an Application scope.
The small "abstract override" trick creates a kind of AOP around aspect over
trait GwtApp extends Application{
val gwtDir = new Directory(null, "clap://class/_gwt/")
abstract override def createRoot():Restlet={
gwtDir.setContext(getContext)
super.createRoot match{
case root:Router=>
root.attach("/_gwt", gwtDir);root
case r@_=>
val root = new Router
root.attachDefault(r)
root.attach("/_gwt",gwtDir)
root
}
}
}
createRoot method, see this. So I can just mixin this trait into my application without any change.
class App extends Application with GwtApp{
override def createRoot():Restlet={
val r = // ... get Root as a Router
r.attach("/test", classOf[TestResource])
//attach the main view
r.attach("/main", new Restlet(getContext){
override def handle(req:Request, resp:Response){
val r = new InputRepresentation(
getClass.getResourceAsStream("/host.html"),
MediaType.TEXT_HTML)
resp.setEntity(r)
}
})
r
}
}
Two endpoints are exposed by this application.
/main- return the host page of above GWT client, the src location of GWT bootstrap js file is/_gwt/gr.demo.App/gr.demo.App.nocache.js/test- a very simple restful service acceptingtext/htmlrequest and serving back atext/plainrepresentation, see below;
class TestResource(ctx:Context, req:Request, resp:Response)
extends Resource(ctx,req,resp){
getVariants.add(new Variant(MediaType.TEXT_HTML))
override def represent(v:Variant)={
new StringRepresentation("Helo@"+new Date, MediaType.TEXT_PLAIN);
}
}
After you package all of above into a Component;
val com = new Component
Array(HTTP, FILE, CLAP).foreach(x=>com.getClients.add(x))
com.getServers.add(HTTP, 8080)
com.getDefaultHost.attachDefault(new App)
You get a client served by pure Restlet application.
So next post of this series will be the prototype of serving GWT RPC using Restlet, hopeful it will be cool.
Posted by Unknown at 1:18:00 PM 0 comments
2008/10/30
Restlet-GWT, the compiled size
As mentioned in my previous post, I would like to check the code size generated by GWT if Restlet-GWT is used. So here are two code snippets.
The obf style code size are (not gziped):
- For pure GWT, the size is around 30K;
- For Restlet-GWT, the size is around 135K;
Posted by Unknown at 1:49:00 PM 0 comments
Restlet 1.1.0 released
Got notification from Jerome Louvel on Linkedin, that Restlet 1.1.0 was officially released on 28 th, Oct. You can check detail what's-new there.
The new Restlet-GWT module interests me most. Thanks to the GWT 1.5's support of Java 5 syntax, so by using this new Restlet-GWT, you can almost seamlessly create a restful client for both browser and normal application by using one set of code. Which means you can create only one client API for your restful application/services (what ever) and says hey this is the client of my RIA, you can use it in your GWT client which runs inside browser or use it in your Eclipse RCP client.
I have to say I haven't tried it yet. I am wondering whether it would be too heavy for a web application.
From the programming model perspective, there is no big difference between Restlet-GWT with GWT's own RequestBuilder. For pure GWT, you need to deal with plain text for both of request and response. For Restlet-GWT, there is a abstraction called Representation.
Both of them require a callback object to receive responses. One advantage is that Restlet's response is content type aware (json, xml).
Posted by Unknown at 12:04:00 PM 0 comments
2008/10/29
GWT RPC from non-servlet-container (1)
GWT is indeed a beautiful toolkit which does perfect jobs on client side. The most sparking point which I like most is that it is ALL ABOUT JAVA. So it inherits all those beautiful, highly efficient, well-thought-and-examined Java best practices, besides you have so many brilliant tools to use. And BANG!, it compiles cross-platform javascript codes which are run on most of popular browsers nicely. Since it is pure HTTP stuff, it can talks any kind of backend services via web using standard tech such as XML or JSON.
One minor speck is on GWT's proprietary RPC function, in where the backend must be Java and running in a servlet container. There might be some efforts taken on somewhere to let GWT RPC talk with services done other than Java, for example this one. I would like to try something simpler, let GWT RPC being served by non-servlet-container. Although servlet DO rule the market, but still there is alternative, such as Restlet.
The not so much a secret of GWT RPC
Thanks for GWT team's great job, the implementation of service-side GWT RPC is such a breeze which has good structure and not so difficult to get extracted out to be used outside servlet-container.Actually, the key part of a RPC call is consists of following statements:
For
RPCRequest rpcRequest = RPC.decodeRequest(payload, this.getClass(), this);
RPC.invokeAndEncodeResponse(this, rpcRequest.getMethod(),
rpcRequest.getParameters(), rpcRequest.getSerializationPolicy());
RPC.decodeRequest method:- The first argument, the
payload, is a UTF-8 string directly read from the input stream client request, no weird encode/decode in it; - The second argument is the class which implements this RPC's
RemoteServiceinterface. So it is the target class in the RPC method is defined; - The last argument should be an instance of
SerializationPolicyProvider. It provides an instance ofSerializationPolicywhich instructs how to serialize a Java object from/to a string payload. This is one of the tricky parts which I will explain later.
The
RPC.decodeRequest method returns an instance of RPCRequest which takes all necessary data for the RPC call.The second statement is easier to understand, in which the first argument is the target instance on which this RPC call is to be invoked. The
RPC.invokeAndEncodeResponse methods also return a UTF-8 string as result in JSON format (either success or failure). This string can be written back directly to GWT client (the current implementation is to gzip it first).According to aforementioned, seems we can serve a GWT RPC call from an non-servlet container by:
- Prepare an instance of
SerializationPolicyProvider - Get to know where to load your class and how to create an instance from it;
- Read a UTF-8 string from HTTP request;
- Create an instance of
RPCRequestand callRPC.invokeAndEncodeResponseas illustrated above. - Write the UTF-8 string back to HTTP response, which content-type must be
application/json;charset=utf-8
Where SerializationPolicy come from
SerializationPolicy is something new after 1.4.RPC now generates a serialization policy file during compilation. The serialization policy file contains a whitelist of allowed types which may be serialized. Its name is a strong hash name followed by.gwt.rpc. This file must be deployed to your web server as a public resource, accessible from a RemoteServiceServlet viaServletContext.getResource(). If it is not deployed properly, RPC will run in 1.3.3 compatibility mode and refuse to serialize types implementing Serializable. (#1297)
Basically, for each
RemoteServiceServlet implementation, there is one policy file generated which has a strong hash name. This name is passed by the RPC request payload. We can then use it together with the utility method SerializationPolicyLoader.getSerializationPolicyFileName to load a SerializationPolicy instance. The question is where to find that file under our non-servlet-container environment (says Restlet) and how smart it could be.The next installment I am going to talk about how to create a GWT application backending with Restlet. Hope it will be fun!
Posted by Unknown at 4:41:00 PM 0 comments