Posts Tagged GWT

GWT 2.0 RC2 available

Logo GWTThe release is coming closer… Christmas gift anyone?

GWT 2.0 RC2 is available from this link. The Google plugin for Eclipse has been updated as well to 1.2 RC2

Caution Windows users who installed the IE plugin, you’ll have to uninstall it and install a new version (details there).

, ,

No Comments

The book is out… Le livre est sorti…

The same day as GWT 2.0 RC1, that has to be a good sign…

(cliquez sur le livre pour feuilleter une sélection de pages)

,

2 Comments

GWT 2.0 RC1 is out!

Logo GWTBruce Johnson:

Hi folks!
GWT 2.0 RC1 is now ready for you to try. The full documentation is still very much a work in progress, but you can at least start trying out the GWT SDK distribution and the Google Plugin for Eclipse using the following
instructions:
http://code.google.com/p/google-web-toolkit/wiki/GWT_2_0_RC
GWT 2.0 is going to be a big release, so don’t be surprised if there are a few bumps and surprises as we continue to finalize everything.

Read full announcement on GWT group


,

6 Comments

GWT is the present of web development

Logo GWTI’ve read a few articles by people who didn’t like GWT, but usually I don’t care to reply because it requires a lot of time to do it properly. However, today I came across this article on dzone: Lost in Translation or Why GWT Isn’t the Future of Web Development.

This one was troubling because it made good points on the way, some that I can’t agree more with (like the quality of Ext-GWT…) but it comes to a completely biased conclusion about GWT. So I took some time and wrote a reply

,

1 Comment

GWT: generating the factory

Controllers galore

In my current GWT project, we make heavy use of MVC, which means we have controllers and views all over the place. To link menu items and other actions to screens, the action item only keeps a reference to the controller class. This implies that we have to be able to instantiate the controller when the action is triggered.

No problem, we have a controllerClass, so we call controllerClass.newInstance(), right?

Well, not in the GWT world.

When you’re running the web version of your GWT application, due to the amount of meta-data that would have to be carried together with your code, Java reflection capabilities are not available. Which means that, even though the Class class exists, you can’t use it to list methods or fields, and you definitely can’t use it to obtain an instance of that class. This is a classic trap, because it might appear to work in hosted mode, simply because hosted mode runs in a standard JVM… but when you try to compile and run your application in web mode, you can forget it.

The poor man’s dynamic instantiation

Basically, the only option you have when you want to instantiate a class from a class reference, is to compare it with known classes, and when one is recognized, to call “new”, like this:

if (controllerClass == Controller1.class) {
  return new Controller1();
}
if (controllerClass == Controller2.class) {
  return new Controller2();
}
// (many lines omitted...)
throw new IllegalArgumentException("The class " +
 controllerClass.getName() + " is unknown in this factory.");

You can see how this is painful:

  • whenever you create a new controller, you have to remember to add it to this method, otherwise instantiation will fail during runtime
  • this method rapidly grows and becomes unmaintainable

To make things as clean as possible, this should be wrapped in a controller factory: a simple class that implements an interface with a single method

Controller createController(Class<? extends Controller> controllerClass) throws IllegalArgumentException;

While this hides the “details” of the implementation, it does not remove the burden of maintaining the factory method.

This is when GWT’s deferred binding mechanism can be useful. To compensate for the lack of runtime introspection, the GWT compiler can dynamically (during compilation) provide the best-suited implementation of an interface, or subclass of a given class. The client requests an implementation of this class by calling GWT.create(MyInterface.class).

The replacement class can be provided in one of two ways:

  • by substitution: under certain conditions, the class is substituted with an existing implementation. This is how GWT internally handles differences between browsers.
  • by generation: the implementation is constructed at compile-time by a Generator.

So why not use the second option and let the GWT compiler generate the factory class for us? It’s simple enough, we just need to generate an if statement for each known controller. So let’s try to do that.

Declaring the generator

First we need to tell the GWT compiler to invoke our generator, and when. Suppose our factory method is contained in an interface named ControllerFactory, then we just need to add the following lines to the GWT module file:

    <generate-with class="oge.gwt.rebind.ControllerFactoryGenerator">
         <when-type-assignable class="oge.gwt.client.ControllerFactory"/>
    </generate-with>

This translates to “when the client calls GWT.create() with a parameter class that is assignable to ControllerFactory, use ControllerFactoryGenerator to build the implementation class and return an instance of this class”. The interface must reside in the client package, but the generator must be somewhere else. The common practice is to put all generators in a package named “rebind”.

Next the generator itself.

Building the generator

The generator must implement the interface com.google.gwt.core.ext.Generator, which defines a single method:

generate(TreeLogger logger, GeneratorContext context, java.lang.String typeName)

where:

  • logger is used to log debug/info messages
  • context is used to obtain context information, and helpers to write the generated class
  • typeName is the name of the type being substitued; in our case this should always be “oge.gwt.client.ControllerFactory”

This method is invoked by the GWT compiler whenever the generate-with clause of the module is activated. The generated class must be written as full java source like you would write by hand; luckily some convenience methods exist.

The first thing the generator needs to decide is what the name of the generated class will be. The common practice is to add the “Impl” suffix to the interface name (we use “GeneratedImpl” to distinguish it from our hand-coded implementation).

This is how we do it:

public String generate(TreeLogger logger, GeneratorContext context, String typeName) throws UnableToCompleteException {

 logger.log(TreeLogger.Type.INFO, "Generating " + typeName);

 this.typeName = typeName;
 TypeOracle typeOracle = context.getTypeOracle();

 try {
 // obtain JClassType instance for target type and construct target class name
 JClassType classType = typeOracle.getType(typeName);
 this.packageName = classType.getPackage().getName();
 this.className = classType.getSimpleSourceName() + "GeneratedImpl";

 // generate class source code
 generateClass(logger, context);

 } catch (Exception e) {
 logger.log(TreeLogger.ERROR, "Failed to generate implementation for " + typeName, e);
 }

 // return the fully qualifed name of the generated class
 return this.packageName + "." + this.className;
 }

Things to note:

  • we use TypeOracle (from the context) to obtain a JClassType instance for the type being substituted. TypeOracle and the associated classes (JClassType, JPackage, JField, JConstructor, JMethod, etc.) provide a powerful tool to query Java source and meta-information. It can be seen as the equivalent of native Java introspection. Once we have our JClassType instance, we can easily obtain the package name and class name.
  • if the generation succeeds, the method must return the fully qualified name of the generated class

Generating the class

The class generation itself requires the following steps:

  1. Begin source generation by calling context.tryCreate(logger, packageName, className). This method returns a PrintWriter, which we will use to write the source code. If it returns null, it means that the target class has already been generated and we can safely skip the generation.
  2. Instantiate a ClassSourceFileComposerFactory with the package name and class name. This is a utility class that helps building the target class.
  3. If necessary, call addImport(),  addImplementedInterface(), setSuperclass() on the ClassSourceFileComposerFactory instance.
  4. Obtain a SourceWriter by calling createSourceWriter(context, printWriter) on the instance of ClassSourceFileComposerFactory.
  5. Use methods of SourceWriter (println(), indent(), outdent(), …) to write the meat of the class.
  6. When you’re done, commit the generated source by calling context.commit(logger, printWriter).

This is how we do it for our controller factory:

private void generateClass(TreeLogger logger, GeneratorContext context) throws Exception {

 // get a PrintWriter to write source code to
 PrintWriter printWriter = context.tryCreate(logger, packageName, className);
 if (printWriter == null) {
 // source code has already been generated, abort
 return;
 }

 ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(packageName, className);
 composer.addImport("oge.gwt.client.mvc.Controller");
 composer.addImplementedInterface(typeName);

 SourceWriter sourceWriter = composer.createSourceWriter(context, printWriter);

 generateCreateControllerMethod(sourceWriter, context.getTypeOracle());

 sourceWriter.println("}");

 // commit source generation
 context.commit(logger, printWriter);
 }

Almost done… now we only need to implement generateCreateControllerMethod(), which will generate the body of the method “createController()”. To do this, we need to generate a block like the following for each controller class that might be instantiated dynamically at runtime:

if (controllerClass == ControllerXXX.class) {
  return new ControllerXXX();
}

To do this, we wil simply iterate on all known classes by using getTypes() from TypeOracle, and retain only those that:

  • implement our substituted interface (ControllerFactory)
  • are public
  • are not abstract
  • have a public no-args constructor

These conditions are checked by using various methods on JClassType.

This is the result:

private void generateCreateControllerMethod(SourceWriter sourceWriter, TypeOracle typeOracle) throws Exception {

 // start method source generation
 sourceWriter.println("public Controller createController(Class<? extends Controller> controllerClass) throws IllegalArgumentException {");
 sourceWriter.indent();

 JClassType simpleViewClassType = typeOracle.getType("oge.gwt.client.mvc.Controller");

 JClassType classTypes[] = typeOracle.getTypes();
 for (JClassType classType : classTypes) {

 // we're only interested in subclasses of simpleViewClassType that are public and not abstract
 if (!classType.isAssignableTo(simpleViewClassType)
 || !classType.isPublic()
 || classType.isAbstract()) {
 continue;
 }

 // check that we have a no-arg constructor
 try {
 classType.getConstructor(new JType[] {});
 } catch (NotFoundException nfe) {
 continue;
 }

 System.out.println("Found controller: " + classType);
 sourceWriter.println("if (controllerClass == " + classType.getQualifiedSourceName() + ".class) {");
 sourceWriter.println("  return new " + classType.getQualifiedSourceName() + "();");
 sourceWriter.println("}");
 }

 sourceWriter.println("throw new IllegalArgumentException(\"The class \" + controllerClass.getName() + \" is unknown in this factory.\");");

 // end methods source generation
 sourceWriter.outdent();
 sourceWriter.println("}");

 }

Using the generated factory

Finally, we need to replace our old manual instantiation of the hand-coded ControllerFactoryImpl with a call to GWT.create() so that the generator mechanism is triggered:

//controllerFactory = new SimpleViewControllerFactoryImpl();
controllerFactory = GWT.create(SimpleViewControllerFactory.class);

Now when we GWT-compile the application, we will see our log message indicating that the generator is being called:

 Computing all possible rebind results for 'oge.gwt.client.ControllerFactory'
 Rebinding oge.gwt.client.ControllerFactory
 Invoking <generate-with class='oge.gwt.rebind.ControllerFactoryGenerator'/>
 Generating oge.gwt.client.ControllerFactory

The application should work exactly as before, since we only substituted a hand-written implementation of ControllerFactory with a computer generated version, which does exactly the same.

You will notice that the generator also gets called when you reload the hosted mode window… this makes sense, because you could very well have added a new Controller , in which case the factory needs to be updated. This is the only downside of this approach, but in my opinion the benefits are well worth the added overhead.

, , ,

10 Comments

GWT 2.0 milestone 2

Logo GWTJohn Labanca:

We are excited to release the second milestone build for GWT 2.0
today. This milestone is essentially feature complete, and provides
somewhat more stability in the various bits of core functionality that
will be coming in GWT 2.0.

The full announcement in GWT groups

The download

,

No Comments

Announcing GWT 2.0 Milestone 1

Logo GWT
Amit Manjhi:

Hi everyone,
We are excited to release the first milestone build for GWT 2.0 today. This milestone provides early access (read: known to still be unfinished and buggy) to the various bits of core functionality that will be coming in GWT 2.0. Please download the bits from:

http://code.google.com/p/google-web-toolkit/downloads/list?can=1&q=2….

(…)

Announcing GWT 2.0 Milestone 1 – Google Web Toolkit | Google Groups.

,

2 Comments

Livre sur GWT: la sortie s’approche

Couverture(For the english speaking crowd: this is about the book I’m writing about GWT in french)

Le livre que je suis en train d’écrire portera sur GWT 1.7 et 2.0, et s’adressera plutôt aux développeurs Java qui souhaitent passer à GWT. La sortie est prévue en novembre aux éditions Dunod.

Le bout du tunnel approche ! Je mets actuellement la dernière main aux nouveautés de GWT 2.0, OOPHM, code splitting, UiBinder & co.

Pour info vous pouvez déjà précommander sur Amazon :)

, ,

2 Comments

Building GWT on the Mac

If you try to build the latest GWT from the svn repository on a Mac, you may end up with a bunch of errors like the following:

BUILD FAILED
/Users/ogerardin/Documents/GWT 2/trunk/build.xml:105: The following error occurred while executing this line:
/Users/ogerardin/Documents/GWT 2/trunk/build.xml:27: The following error occurred while executing this line:
/Users/ogerardin/Documents/GWT 2/trunk/build.xml:58: The following error occurred while executing this line:
/Users/ogerardin/Documents/GWT 2/trunk/platforms.ant.xml:36: The following error occurred while executing this line:
/Users/ogerardin/Documents/GWT 2/trunk/platforms.ant.xml:13: The following error occurred while executing this line:
/Users/ogerardin/Documents/GWT 2/trunk/dev/core/build.xml:192: The following error occurred while executing this line:
/Users/ogerardin/Documents/GWT 2/trunk/common.ant.xml:277: Non-zero status code result (1) running command: svn info

The last line indicates a problem with the “svn” command. The problem is that Mac’s builtin svn client is version 1.4.4 (wich you can check by typing “svn --version” in a terminal), but to build GWT, you need a newer svn client.

The steps to fix this are:

  • go to http://www.open.collab.net/downloads/community and download the latest subversion binaries for Mac OS X
  • you will get a disk image file; mount it and run the included installer. It will install to /opt/subversion/bin
  • edit your ~/.profile file to add the following line:
export PATH=/opt/subversion/bin:$PATH
  • start a new shell or type “. ~/.profile” to make sure the change is loaded in the current shell
  • type “svn --version” and check that the version is the latest one (1.6.5 as of today)
  • cd to your checked out GWT trunk directory, type “ant” and watch GWT build :)

, , , ,

No Comments

GWT + Snow Leopard: fixed with GWT 1.7.1

mac-logoGoogle has just released GWT 1.7.1 to fix the issue I mentioned a few days ago regarding Snow Leopard compatibility.

In short, you can now run GWT on Snow Leopard using the Java command line argument -d32 without further modification. The GWT SDK no longer directs you to only use Java 5.

http://googlewebtoolkit.blogspot.com/2009/09/gwt-171-release-fixes-mac-os-x-snow.html

,

No Comments