If you're curious what generators are, or have never used them with GWT, this simple generator will give you a quick introduction to get your hamster wheels turning.
I do a lot of prototyping in GWT before I build the backend and I've recently had the need to generate a "build version" for my GWT interface. While there are about fiftybazillionmillion different ways to do this, I've chosen a GWT generator to create a buildstamp. This
lets me tar up the www results of clicking on "Compile/Browse" and fire them off for others to play.
In your Java code, you just add:
...
BuildStamp buildStamp = GWT.create(BuildStamp.class);
yourLogo.setTitle(buildStamp.getBuildStamp());
...
This will add a tooltip to yourLogo which looks like: "Built on kikai.linuxstuff.org at Tue Jul 29 19:07:05 PDT 2008".
When you compile your GWT application the generator is compiled and run. It generates Java code which is converted to JavaScript along with the rest of your application. When this happens the timestamp is "frozen".
Code and wiring follows. Imports omitted, use your favourite IDE to fill them in. This makes a pretty simple Hello World example.
Comments are more than welcome.
package org.example.rebind;
public class BuildStampGenerator extends Generator {
private String className;
private String packageName;
@Override
public String generate(TreeLogger logger, GeneratorContext context, String typeName)
throws UnableToCompleteException {
try {
TypeOracle typeOracle = context.getTypeOracle();
JClassType classType = typeOracle.getType(typeName);
packageName = classType.getPackage().getName();
className = classType.getSimpleSourceName() + "Impl";
generateClass(logger, context);
} catch (Exception e) {
logger.log(TreeLogger.ERROR, "Exception during BuildStamp creation.", e);
}
return packageName + "." + className;
}
private void generateClass(TreeLogger logger, GeneratorContext context) {
PrintWriter printWriter = context.tryCreate(logger, packageName, className);
/* Returns null if the package has already been generated. */
if (printWriter == null) {
return;
}
ClassSourceFileComposerFactory composer =
new ClassSourceFileComposerFactory(packageName, className);
// shouldn't magic just happen here?
composer.addImplementedInterface(BuildStamp.class.getCanonicalName());
SourceWriter sourceWriter = composer.createSourceWriter(context, printWriter);
writeGetBuildStamp(sourceWriter);
sourceWriter.outdent();
sourceWriter.println("}");
context.commit(logger, printWriter);
}
/**
* Write the source code for {@code BuildStamp#getBuildStamp()}.
*/
private void writeGetBuildStamp(SourceWriter sourceWriter) {
sourceWriter.println("public String getBuildStamp() {");
sourceWriter.println(" return \"" + Generator.escape(getBuildStamp()) + "\";");
sourceWriter.println("}");
}
/**
* Executed at *compile* time, this generates a string that is embedded in the
* generated class. In hosted mode, this code is rebuilt each Refresh.
*
* @return A user-friendly build string, good for hiding in tooltips.
*/
String getBuildStamp() {
StringBuilder msg = new StringBuilder();
msg.append("Built on ");
try {
String hostname = InetAddress.getLocalHost().getCanonicalHostName();
msg.append(hostname);
} catch (UnknownHostException e) {
msg.append("(hostname not available)");
}
msg.append(" at ");
msg.append(new Date());
return msg.toString();
}
}
You will need a BuildStamp interface:
package org.example.client;
public interface BuildStamp { public String getBuildStamp(); }
And wire it up in your .gwt.xml:
<generate-with class="org.example.rebind.BuildStampGenerator">
<when-type-assignable class="org.example.client.BuildStamp" />
</generate-with>
What do you think?
1 comments:
Thanks for the post. Saved me a few hours work!
Post a Comment