Friday, April 27, 2018

Discussion: gwt-user.jar-like uber jar for T.Broyer's multi-module project.

Any experienced developer tends to reuse existing components and organize his own code to allow reusability in future whenever it is possible. Division of labor further encourages a group of developers to physically split a big project into modules and work independently on each part. I think T.Broyer's multi-module project archetype for Maven introduces a great platform enabling these features and making versioning task look effortless.

However I personally start to have issues with the last part, when it comes to the release. Specifically, should I aggregate the build artifacts into one gwt-user.jar kind of unit? I know it's a broad topic, and there're multiple angles to look at this problem, one of which says "it's not Maven way" (though I start to doubt that I applied "Maven way" concept in the appropriate contex). Therefore I want to hear your advice regarding this matter and avoid reinventing the wheel. If there's a sample project already configured to do the similar task with grace, then I guess a link to github will be sufficient.

Imagine you're developing gwt-user. Inside com\google\gwt\user folder you'll find client direcotry and a bunch of <module>.gwt.xml files such as User.gwt.xml, UI.gwt.xml, TextBox.gwt.xml. The latter contains a bunch of deferred binding instructions, e.g.:

  <!-- IE has a completely different TextBox implementation -->
 
<replace-with class="com.google.gwt.user.client.ui.impl.TextBoxImplIE8">
   
<when-type-is class="com.google.gwt.user.client.ui.impl.TextBoxImpl"/>
   
<when-property-is name="user.agent" value="ie8"/>
 
</replace-with>

User inherits UI and UI inherits TextBox. But then we also have a subfolder datepicker with DatePicker.gwt.xml and it's own client directory.

So my view on this is that you don't wanna ship datepicker.jar to your clients, but instead combine the components from all related projects and modules into one library.

--------------------------------------------------------------------------------------------------------------------------------

Further in the text I'm gonna present my hack to multi-module archetype build configuration based on maven-shade-plugin, so let's get into details.

There were three strong candidates to do this job: maven-jar-plugin, maven-assembly-plugin and maven-shade-plugin. However the latter was specifically made for:
- This plugin provides the capability to package the artifact in an uber-jar, including its dependencies and to shade - i.e. rename - the packages of some of the dependencies.
- The Shade Plugin has a single goal: shade:shade is bound to the package phase and is used to create a shaded jar.

In multi-module structure of my sandbox project I have two gwt-lib packaged modules my-client-lib and my-client-lib-a. The task is to gather their build artifacts inside one jar. My solution was to create a subsidiary module sandbox-gwt (though it could as well be another project) with the same groupId as my-client-lib* and to include only such artifacts in the uber-jar. Making it this way automates the task of naming by exploiting ${project.groupId} and ${project.version} properties.

pom.xml of sandbox-gwt module:

<project xmlns="http://maven.apache.org/POM/4.0.0"
 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 
<modelVersion>4.0.0</modelVersion>
 
<parent>
   
<groupId>com.example.project</groupId>
   
<artifactId>sandbox</artifactId>
   
<version>0.0.1-SNAPSHOT</version>
 
</parent>
 
<groupId>net.coolcode.gwt</groupId>
 
<artifactId>sandbox-gwt</artifactId>

 
<dependencies>
   
<dependency>
     
<groupId>${project.groupId}</groupId>
     
<artifactId>my-client-lib</artifactId>
     
<version>${project.version}</version>
   
</dependency>
   
<dependency>
     
<groupId>${project.groupId}</groupId>
     
<artifactId>my-client-lib-a</artifactId>
     
<version>${project.version}</version>
   
</dependency>
 
</dependencies>

 
<build>
   
<plugins>
     
<plugin>
       
<groupId>org.apache.maven.plugins</groupId>
       
<artifactId>maven-shade-plugin</artifactId>
       <version>3.1.1</version>
       <configuration>
         <artifactSet>
           <includes>
             <include>${project.groupId}</include>
           </includes>
         </artifactSet>
         
<createDependencyReducedPom>true</createDependencyReducedPom>
       
</configuration>
       
<executions>
         
<execution>
           
<phase>package</phase>
           <goals>
             <goal>shade</goal>
           </goals>
         </execution>
       
</executions>
     
</plugin>
   
</plugins>
 
</build>
</project>

artifactSet - Artifacts to include/exclude from the final artifact. Artifacts are denoted by composite identifiers of the general form groupId:artifactId:type:classifier.

But there's a set of techniques mentioned in different sources, which might do the task better?! Those include the use of:
- <profiles>
- <executions>
BOM parent to manage dependencies better. I think it also helps to keep versioning clean.

--------------------------------------------------------------------------------------------------------------------------------

I also noticed, that gwt-maven-plugin can generate some code in <actual-module-name>.gwt.xml, such as adding

<inherits name="com.google.gwt.core.Core"/>

In the context of a large uber-jar such as gwt-user.jar if it could establish the crossreferencing between modules based only on Maven dependencies, that would be interesting. E.g. to put 

<inherits name="net.coolcode.gwt.mvp.MyCoolCodeAppA" />

inside MyCoolCodeApp.gwt.xml, if my-client-lib depends on my-client-lib-a.

--------------------------------------------------------------------------------------------------------------------------------

Lastly I'm curious, in a context of development of gwt-user project it seems that due to an unconventional for GWT applications module structure (src/main/module.gwt.xml not src/main/package/MyModule.gwt.xml) if we want to strictly follow the archetype's idiom, we must have one module per one <module>.gwt.xml, e.g. TextBox.gwt.xml. I'm not sure what I'm trying to ask, but is it the way? And since we manually manage <source path="client" /> in module.gwt.xml, is it so evil to put server-lib artifacts in the same uber-jar? 

--------------------------------------------------------------------------------------------------------------------------------
p.s. It actually began to get into my habit to revisit this archetype once in a while, and then find myself seriously confused shortly after ^^.

--
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-toolkit+unsubscribe@googlegroups.com.
To post to this group, send email to google-web-toolkit@googlegroups.com.
Visit this group at https://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/d/optout.

No comments:

Post a Comment