Dmitrii Tikhomirov
Posted on June 6, 2022
Disclaimer: Initially this article has been published by me on blog.kie.org. I republish it here with their permission.
It looks like 15 years of GWT are coming to the end, and besides that web development has dramatically changed since 2006. There is now no chaos of conflicting browser implementations that require to run multiple permutations each. At the same time modern web development frameworks are far from ideal. For instance, a very strong advantage of GWT was the ecosystem around Maven – the stability and usability of this solution was incredible, especially when big teams worked on large projects.
Google, the main developer of GWT, left the project and started J2CL, the successor of GWT, which takes the very best practices to a new level. Their documentation calls it out as being used in many high performance projects such as Gmail, Inbox, Docs, Slides, and Calendar.
Initially J2CL was developed to be used in the Bazel environment. After several years of hard work the community, led by Colin Alworth, released the first public J2CL version for Maven – J2CL Maven plugin.
So let’s take a look at what it is and how it works.
J2CL AND CLOSURE COMPILER
J2CL is responsible for only one task – to transpile a set of Java classes into a set of JavaScript files.
Google’s Closure Compiler is responsible for merging this set of javascripts into one executable JS script, its optimization and minification.
Closure Compiler is extremely efficient in minification and optimization of the JavaScript, it simply has no competitors.
GENERATING OUR FIRST J2CL PROJECT
Let’s start from a simple one module application. Luckily for us, we can generate it from a pre-build archetype. Download the archetype if you don’t have it:
mvn org.apache.maven.plugins:maven-dependency-plugin:get \
-DrepoUrl=https://repo.vertispan.com/j2cl/ \
-Dartifact=com.vertispan.j2cl.archetypes:j2cl-archetype-simple:0.19
Now we can generate a simple application:
mvn archetype:generate -DarchetypeGroupId=com.vertispan.j2cl.archetypes \
-DarchetypeArtifactId=j2cl-archetype-simple \
-DarchetypeVersion=0.19
Let’s take a look at the result:
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── org
│ │ └── treblereel
│ │ └── j2cl
│ │ ├── App.java
│ │ └── App.native.js
│ └── webapp
│ ├── WEB-INF
│ │ └── web.xml
│ ├── css
│ │ └── simpleapp.css
│ └── index.html
└── test
└── java
└── org
└── treblereel
└── j2cl
└── AppTest.java
– App.java is a starting point of our application and there’s one point I have to highlight below.
– App.native.js used to specify how to start our application to the Closure Compiler, because it knows nothing about it. Usage of native.js is a very large topic and a separate article can be written about it.
– AppTest.java is just a J2CL-compatible unit test that runs in HtmlUnit, it’s also possible to use ChromeDriver to run it in a real browser but it takes longer.
– pom.xml – here the only interesting part for us is the j2cl-maven-plugin section. For now it contains only the <compilationLevel>
declaration used to set which level of optimization we are going to use during the compilation of the project. ADVANCED is the most efficient one, so Closure Compiler does aggressive renaming, dead code removal, global inlining and so on. But in some cases Closure Compiler needs our help and care – we have to declare which properties or methods should not be removed or renamed. BUNDLE is less strict and better suitable for development because each compilation round takes less time compared to ADVANCED.
RUNNING AND BUILDING OUR J2CL APPLICATION
j2cl-maven-plugin allows us to run our application in the development mode with build-in hot code reload and source map debug. To start devmode, run the following command in the terminal:
mvn j2cl:watch
When the application started, run the following command in the second terminal:
mvn jetty:run
There is no need to run mvn clean
each time because J2CL will recompile everything from scratch, and we can reuse the results from the previous run. Moreover, there is an option to use global cache between several projects to reduce compilation time.
To build .war
we should run mvn package
, there is nothing new here, everything is pretty familiar to GWT developers.
OK, WHAT IS NEW COMPARED TO GWT
- GWT modules are gone, yes, no more modules. So J2CL will try to compile direct and transitive dependencies from pom.xml, that is why we should set
provided
scope to annotation processors and shade them. -
GWT.create
gone as well - GWT generators are gone, now we should use APT-based generators.
- What about GWT components and widgets we used before ? Most of them have been ported to J2CL.
- Does
@GwtIncompatible
work? Yes, it is still here.
AND WHAT IS THE VALUE OF IT FOR US?
Right now we are focused on migration of our existing projects from GWT2 to J2CL. There are many libraries that have been migrated to J2CL and many other libraries support both GWT2 and J2CL. I'd like to highlight elemental2-* wrappers, DominoKit, Nalu, and many others.
GWT2 has been ported as the GWT Project which is a set of migrated modules. The Errai project, once a very popular dependency injection framework, is the core component of applications we use internally. Errai has been re-implemented as the Crysknife project.
In the upcoming posts I'm going to address several topics:
- Existing libraries and frameworks that are J2CL-compatible
- Defines – how to propagate variables to J2CL
- How we can improve generated code with native.js
- Externs – why do we need them and how to write our own externs
- Semi-reflection
- Interoperability with TS
- And maybe many more
If you like my articles, feel free to join GWT and J2CL channels, follow me on Twitter, LinkedIn or GitHub or even support me on Patreon or Stripe.
Posted on June 6, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.