GWT Multi-Project Deployment


(Ian) #1

Hi Here,

I have been given the task in researching getting our Ant based GWT project int Gradle and so far, everything I have seen about gradle, I have loved however I’m struggling with the “correct” approach as opposed to a “it works” approach and want to achieve a nice rich decoupled set of projects.

The problem we have (as will most GWT projects) are that we use a GWT Compiler that reads the byte & source code to build the javascript output files that are published to the ROOT of the Web-App application

As we use shared byte code for the GWT-RPC calls (rich DTO), these are a dependency for the GWT-Compiler but also need to be web-application for the server. this is currently located with the GWT client java files.

So far; simple I would say - we need 2 distinct projects,

:1 - :Client with no dependencies. I can get the Gwt-Js-Files to be outputted which was quite trivial when following http://goo.gl/INbkj :2 - :Server; project responsible for compiling the server code and build the web application; as with the Client DTO Objects, this has a dependency on :Client & this uses the WAR Plugin

We believe this is the best structure at this time (there was talk about exploding the Shared but this introduces a whole new set of issues).

I can get close but each I fail based on a few key but important issues requirements.

  • As :Client is a dependency on :Server, the GWT Byte code & the GWT-Compiler external libraries are getting included in the artefact, this is not correct as we don’t need the the compiler dependencies or the client byte code but we do need the shared DTO byte code and the JS Code - so for example, we only need all the byte code from com.example.gwt.shared.** included in the web-application…

  • As the compileGWT is a dependency that needs to run after the compileJava task - i need to “Tag” on the post process of calling the compileGWT task when the process has finished…

Sorry for the essay - my questions are;

1: From the output of :client, can i only take a subset of the byte code elements (the com.example.gwt.shared.*) and put them in the web-app from the :server build script?

2: Can I stop the WAR plugin in :server for taking the :client GWT-Compiler dependency and putting it as a dependency of the web-app?

3: Can I get the WAR plugin to copy the :client JS output, e.g. something like

from :client/$gwtOutputDirectory

?

4: Can I get the :Client to execute the gwtCompile task when the javaCompile process finished and for the gwtCompile to get a handle on the output bytecode directories?

At this stage - i’m cautious to suggest we take the “Jump” at this time but know what i’m looking is feasible and know gradle is what we need but don’t want to land myself with a “dirty” gradle build script which going forward is a nightmare to maintain (like out build.xml script today :slight_smile: )

Any advice, no matter how short or simple would help me greatly in this.

Many Thanks and thanks for reading,

Ian…


(Rolf Suurd) #2

Q1.

You should be able to put :client as a providedCompile dependency so that the client jar will be excluded from the final WAR file. You could also opt to disable the jar task for :client alltogether

Q2. For compiling you should make a separate configuration (called gwt) and add the gwt compiler deps to that. You can add the jars in that configuration to your ant classpath when compiling.

Q3 If you have defined outputs for your gwt compile task, you can then inform your war task to take that output (something like: war.from(project(‘client’).tasks.gwtCompile.outputs.files))

This will also automatically cause the compile task to be executed when you inform gradle to assemble a war.


(Ian) #3

Thanks very much for the above, very greatly appreciated; everything is falling into place with the suggestions and the war is very nearly fully functional; but If I may, I have a follow up question;

I need to get a subset of the byte code output from :client project into the war as a jar, e.g. com.example.gwt.shared

Current none of the :client byte code s being added as I have marked the project as providedCompile to stop the dependencies & the complete byte code set from being copied across…

Question: How would that be possible to copy a subset of the byte-code with the following process steps :copy client-class-output->package as jar ->put in WEB-INF/Lib ??

Thanks again

Ian.


(Rolf Suurd) #4

You could introduce a Jar that contains only the shared code.

Since you probably want it to be a project dependency, i think you’ll need to configure and publish it as a separate configuration, and then declare an additional project dependency on that configuration.

In your client project:

configurations {
 shared
}
  task sharedJar(type: Jar) {
 classifier = 'shared'
 from sourceSets.main.output {
  include 'path/to/shared/**'
 }
}
  artifacts {
 shared sharedJar
}

And then in the project that needs the stuff in the war, add

compile project(path: ':client', configuration: 'shared')

To the dependencies. I’m not sure if this will pull in any transitive dependencies needed for the shared code (if any)

Greets Rolf


(Ian) #5

Hi Rolf,

Thanks again; how I got it work was put the following line in the web-app war task

classpath project(':client').sourceSets.main.output.classesDir

So that pulled all the byte code into the war files classes directory, however also included the redundant byte code & much prefer your approach!!

1 quick question for my gradle learning - could I have used the classpath approach but only include com.example.gwt.shared.* files from the client output to be copied to the classes directory - i’ve been looking for the solution but haven’t came across it yet?

Thanks Again,

Ian.


(Rolf Suurd) #6

Section 44.4.4. File dependencies in the userguide shows an approach about adding dependencies on files.

Maybe something like this would’ve worked:

classpath project(':client').fileTree(dir: project(':client').sourceSets.main.output.classesDir, include: 'path/to/shared/**')

But that’s just a wild guess :slight_smile:


(Ian) #7

Nice one - thanks for everything Rolf - I used your multiple configuration approach and all worked a treat, except got a null pointer exception in the jar task (something to do with the include nested in the sourceSets) but by replacing it with the below worked a treat - thanks again for your expertise…

task sharedJar(type: Jar) {
    baseName = 'sharedJar'
    dependsOn classes
    from sourceSets.main.output
    include('com/example/gwt/shared/**')
}