Showing posts with label GWT. Show all posts
Showing posts with label GWT. Show all posts

2008/11/24

GWT-ENT 看上去挺有趣

GWT-Ent,一个旨在把GWT带向企业级应用的项目,最近在GWT maillist中宣布了一个很有的工具-GWT AOP。

GWT AOP借鉴了AspectJ 5的annotation定义,实现了5个常用的Advice:

  1. @Around
  2. @Before
  3. @After
  4. @AfterReturning
  5. @AfterThrowing
至于Pointcut的描述,GWT AOP同时支持AspectJ的pointcut表达式和Google Guice的matchclass(我和Guice不熟)。

我还没有想清楚GWT AOP能带来如何显著,壮观的效果,但是这的确是一个GWT generator的非常不错的用例。

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/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;
A bit helter-skelter, isn't it?

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 @Endpoint which 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);
  • @UriPart can be used to indicate how to replace url template with values specified by method parameters, so the method signature now becomes void getCI(@UriPart("type") String type, @UriPart("id") String id);
  • Finally, the interface containing the method needs extends interface org.edgebox.gwt.rest.client.RestRemote as a flag;
Up-to-now, an endpoint to do a HTTP Call is declared as

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);
}
Now, to use this service, using GWT RPC style:
MyService service = GWT.create(MyService.class)
RequestCallback callback = //...
service.getCI("person", "111", callback)
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 http://edgebox.googlecode.com/svn/trunk/edgebox-restgwt for a look.

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

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:


RPCRequest rpcRequest = RPC.decodeRequest(payload, this.getClass(), this);
RPC.invokeAndEncodeResponse(this, rpcRequest.getMethod(),
rpcRequest.getParameters(), rpcRequest.getSerializationPolicy());

For 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 RemoteService interface. 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 of SerializationPolicy which 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:

  1. Prepare an instance of SerializationPolicyProvider
  2. Get to know where to load your class and how to create an instance from it;
  3. Read a UTF-8 string from HTTP request;
  4. Create an instance of RPCRequest and call RPC.invokeAndEncodeResponse as illustrated above.
  5. 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 via ServletContext.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!

Footer

  © Blogger template 'Grease' by Ourblogtemplates.com 2008

Back to TOP