How to pass a build number to a gwt app?

179 views
Skip to first unread message

Bruno Salmon

unread,
Mar 12, 2018, 1:43:09 PM3/12/18
to GWT Users
hi,

I would like my gwt app to be able to identify its version itself, by providing a method that returns the build number.

I'm using Jenkins, maven and the maven plugin for gwt to build my app. Jenkins generates the build number and I know how to pass that build number to maven (using a build.number maven property), but I don't know how I can pass that build.number to the GWT compiler and how to code that method in my java source so it returns that build number.

Any idea how I can do that?

Thomas Broyer

unread,
Mar 12, 2018, 6:27:17 PM3/12/18
to GWT Users
Easiest with recent GWT version would be to declare a <configuration-property>, get it using System.getProperty(), and set it with --property.

With older versions, use Maven filtering on a properties file you use with a com.google.gwt.i18n.client.Constants; or have a look at Mojo's Java template plugin.

Bruno Salmon

unread,
Mar 13, 2018, 5:12:30 AM3/13/18
to GWT Users
Thanks Thomas, I'm using GWT 2.8.2 so I tried your first suggestion and it works :-)

I just declared the property in my gwt.xml module file:

<define-configuration-property name="build.number" is_multi_valued="false"/>

 and set its value (with the maven property which is set by Jenkins) when calling your plugin in the configuation section: 

<configuration>
<compilerArgs>
<compilerArg>-setProperty</compilerArg>
<compilerArg>build.number=${build.number}</compilerArg>
</compilerArgs>
</configuration>

Then System.getProperty("build.number") returns the expected value :-)

Thank you

Joker Joker

unread,
Nov 5, 2020, 2:36:49 AM11/5/20
to GWT Users
I want to share solution for gradle based project. 
This solution allows to get any build's properties on client/server.

1) Add to build.gradle
...
task createProperties(dependsOn: processResources) {
doLast {
 new File("$buildDir/resources/main/yourmodule/shared/IVersion.properties").withWriter { w ->
  Properties p = new Properties()
  p['version'] = project.version.toString()
  p.store w, null
 }
}
}

classes {
  dependsOn createProperties
}
...
2) Add IVersion.java:

package yourmodule.shared;
import com.google.gwt.i18n.client.Constants;
public interface IVersion extends Constants {
   String version();
}

3) Add to your code
...
public static final IVersion versionResource = GWT.create(IVersion.class);
...
4) Get version value on client side:
...
versionResource.version();
...
5) Get version on server side:
...
InputStream stream = this.getClass().getResourceAsStream("/yourmodule/shared/IVersion.properties");
InputStreamReader readerIs;
try {
  readerIs = new InputStreamReader(stream, "UTF-8");
  messageInstance = new PropertyResourceBundle(readerIs);
  messageInstance.getString("version"); 
} catch (Exception e) {
  e.printStackTrace();
}
...

вторник, 13 марта 2018 г. в 13:12:30 UTC+4, Bruno Salmon:

Thomas Broyer

unread,
Nov 5, 2020, 4:05:10 AM11/5/20
to GWT Users


On Thursday, November 5, 2020 at 8:36:49 AM UTC+1, Joker Joker wrote:
I want to share solution for gradle based project. 
This solution allows to get any build's properties on client/server.

1) Add to build.gradle
...
task createProperties(dependsOn: processResources) {
doLast {
 new File("$buildDir/resources/main/yourmodule/shared/IVersion.properties").withWriter { w ->
  Properties p = new Properties()
  p['version'] = project.version.toString()
  p.store w, null
 }
}
}
classes {
  dependsOn createProperties
}
...

I'd rather generate the file earlier and add it to the main sourceSet; also, make your task run only when needed by declaring its inputs and outputs:

(using the Kotlin DSL here)

val createProperties by tasks.registering {
  val outputDir
= file("$buildDir/generated/resources/createProperties/")
  inputs
.property("version", project::getVersion)

  outputs
.dir(outputDir)
  doFirst
{
    val props
= Properties()
    props
.setProperty("version", project.version.toString())
    file
("$outputDir/yourmodule/shared/IVersion.properties").apply {
      project
.mkdir(parentFile)
      writer
().use { props.store(it) }
   
}
 
}
}
sourceSets
{
  main
{
    resources
{
      srcDir
(createProperties)
   
}
 
}
}


either that or have an existing resource file with placeholders, and do replacements in the processResources task:

tasks {
  processResources
{
    filesMatching
("yourmodule/shared/IVersion.properties") {
      filter
<ReplaceToken> {
       
"tokens" to mapOf(
         
"version" to project.version
       
)
     
}
   
}
 
}
}

This would be the equivalent of Maven filtering, in Gradle.

That being said, I'd rather go with -setProperty for GWT, and generate a file that way for server-side use only if needed.

Lothar Kimmeringer

unread,
Nov 5, 2020, 4:38:18 AM11/5/20
to google-we...@googlegroups.com


Am 12.03.2018 um 18:43 schrieb Bruno Salmon:

> I would like my gwt app to be able to identify its version itself,
> by providing a method that returns the build number.
>
> [...]
> but I don't know how I can pass that build.number to the GWT compiler and how to
> code that method in my java source so it returns that build number.

You can use a late binding generator to create a class containing all the
data you need. Google it but here's the one I've created 13 years ago
for the exact reason you've asked about. These were the steps I've taken
(it's been 13 years and hasn't been touched since then so there might be
better ways now):

Create a data-holding class AboutData.java, that contain public members for the
informations you want to provide (let's assume it's in package mypackage.data)

public class AboutData {
/**
* The date, the application was built
*/
public Date buildDate = null;
/**
* The version of the base application being used for building
*/
public String isBuiltVersion = IMainPanel.CONSTANTS.General_Unknown();
/**
* The SVN-revision-number of the version-class of the application
*/
public int isSvnRevision = -1;
}

Create a Generator (I've placed it in package mypackage.rebind):

import a.package.completely.outside.the.gwt.app.Version;
import com.google.gwt.core.ext.Generator;
[...]

public class AboutDataGenerator extends Generator {

/**
* Generate a default constructible subclass of the requested type. The
* generator throws <code>UnableToCompleteException</code> if for any reason
* it cannot provide a substitute class
*
* @return the name of a subclass to substitute for the requested class, or
* return <code>null</code> to cause the requested type itself to be used
*
*/
public String generate(TreeLogger logger, GeneratorContext context, String typeName) throws UnableToCompleteException {
logger.log(TreeLogger.INFO, "Creating AboutData-class with build-informations", null);
TypeOracle oracle = context.getTypeOracle();
JClassType requestedClass = oracle.findType(typeName);
String packageName = requestedClass.getPackage().getName();
String simpleName = requestedClass.getSimpleSourceName();
String proxyName = simpleName + "Proxy";
String proxyClassname = packageName + "." + proxyName;

PrintWriter pw = context.tryCreate(logger, packageName, proxyName);
if (pw == null){
return proxyClassname;
}

ClassSourceFileComposerFactory factory = new ClassSourceFileComposerFactory(packageName, proxyName);

factory.setSuperclass(simpleName);
factory.addImport("java.util.Date");

Calendar c = Calendar.getInstance();

SourceWriter source = factory.createSourceWriter(context, pw);
source.println("public " + proxyName + "(){");
source.indent();
source.println("buildDate = new Date(" + Long.toString(System.currentTimeMillis()) + "L);");
source.println("isBuiltVersion = \"" + escape(Version.getImplVersion()) + "\";");
source.println("isSvnRevision = " + Integer.toString(Version.REVISION) + ";");
source.outdent();
source.println("}");
source.commit(logger);

return proxyClassname;
}
}

To get this generator being executed, add the following line to your you gwt.xml-file:

<generate-with class="mypackage.rebind.AboutDataGenerator">
<when-type-assignable class="mypackage.data.AboutData"/>
</generate-with>

This will generate a class mypackage.data.AboutDataProxy that will be returned instead
of the original class when required. In your actual class where you want to access
the data, you do a GWT.create:

private void refreshValues(){
AboutData data = (AboutData) GWT.create(AboutData.class);
if (data.buildDate != null){
DateTimeFormat format = DateTimeFormat.getFormat(PredefinedFormat.DATE_LONG);
builtValue.setText(format.format(data.buildDate));
}
else{
builtValue.setText(CONSTANTS.General_Unknown());
}
isBuiltVersion.setText(data.isBuiltVersion);
}

That approach is a bit more complicated than using define-configuration but it's
more powerful and e.g. allows you to get all kinds if information using the full
spectrum of what the JVM provides you with (calling a web service, checking
files on Jenkin's local file system, DB-accesses, etc.). Also, because you're
accessing members of a java class, you'll immediately end up with compile errors
if you have typos somewhere.


Cheers, Lothar

Michael Conrad

unread,
Nov 5, 2020, 8:11:25 AM11/5/20
to google-we...@googlegroups.com
Any particular reason to not use an annotation processor? Especially as GWT.create is deprecated?

--
You received this message because you are subscribed to the Google Groups "GWT Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-tool...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-web-toolkit/aa760b0f-d641-4741-bb50-d58fb5585499n%40googlegroups.com.

Joker Joker

unread,
Nov 5, 2020, 11:57:21 PM11/5/20
to GWT Users
Actually there is no reason.
Do you have a ready-made solution based on annotation processor?

четверг, 5 ноября 2020 г. в 17:11:25 UTC+4, m.conr...@gmail.com:
Reply all
Reply to author
Forward
0 new messages