Showing posts with label Restlet. Show all posts
Showing posts with label Restlet. Show all posts

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.

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
}
}
Finally attach ping service to the router of this application and specify the SppHolder dependency:

override def createRoot={
//...
r.attach("/ping", new PingServiceImpl{
def provider = self
})
//...
}
There is no need to change code of GWT client, just let ping service call endpoint http://localhost:8080/ping will done.

For complete source code of this sample, see http://edgebox.googlecode.com/svn/sandbox/gwt-restlet-demo/.

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.

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
}
}
}
The small "abstract override" trick creates a kind of AOP around aspect over 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 accepting text/html request and serving back a text/plain representation, 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.

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 RequestBuilder from GWT core

final RequestBuilder rb = new RequestBuilder(RequestBuilder.GET, URL
.encode("http://localhost:8080/test"));
Button b = new Button("Click me");
RootPanel.get().add(b);
b.addClickListener(new ClickListener() {

public void onClick(Widget sender) {
try {
rb.sendRequest(null, new RequestCallback() {

public void onError(Request request, Throwable exception) {

}

public void onResponseReceived(Request request,
Response response) {
RootPanel.get().add(new Label(response.getText()));
}
});
} catch (RequestException e) {
e.printStackTrace();
}
}
});


The code using Restlet-GWT

final Client client = new Client(Protocol.HTTP);

Button b = new Button("Click me");
RootPanel.get().add(b);
b.addClickListener(new ClickListener() {

public void onClick(Widget sender) {
client.get("http://localhost:8080/test", new Callback() {
public void onEvent(Request request, Response response) {
RootPanel.get().add(
new Label(response.getEntity().getText()));
}

});
}
});



The obf style code size are (not gziped):
  • For pure GWT, the size is around 30K;
  • For Restlet-GWT, the size is around 135K;
Is 100K difference big deal?

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).

Footer

  © Blogger template 'Grease' by Ourblogtemplates.com 2008

Back to TOP