J2HTML (j2html) is a Java library used to generate HTML I have been using it to create Web 1.0 applications in Java. Web 1.0 is server-side rendering pages with minimal Javascript.
As I got deeper into using the library I started to read the actual source code of this library with an eye on following Java best practices and to my pleasant surprise, this code follows many of them.
I am going to show here some examples of using interesting Java features, beyond the basics.
1. Functional Interface
People are aware that Java supports some form of Functional Programming, and here is an example of using it:
@FunctionalInterface
public interface Indenter {
String indent(int level, String text);
}
public static Indenter indenter = (level, text) -> String.join("", Collections.nCopies(level, FOUR_SPACES)) + text;
Things that I noticed:
String has a method called join. I used before StringUtils.join, but now that is in the standard library I don’t need to use that anymore
Collections has the method nCopies, never used it but clever to have it here
2. Passing Configuration to Inner Methods
Passing configuration to various methods is always complicated because:
If we use parameters then there are more and more parameters to pass
Or, we use a static class or thread-storage we add configuration but this just hides a dependency. This is called “Ambient Context” anti-pattern.
Or we use a property with a reasonable default in classes that need it.
J2HTML uses the third approach. There is a class called Config. This is passed as parameter in the Builder constructor and has a “good” default.
Some ideas from the Config class:
All the “good” defaults are exposed as public
There is a “defaults” method that returns a Config with the “good” defaults
Has a static factory method called global
All the operations on config are “copy on write” this avoids concurrency issues
The HtmlBuilder gets the config in the constructor and is optional. If not present it gets the defaults.
3. Renderable, Appendable
J2HTML uses several “marker” interfaces.
Renderable is an interface shared by all the Tags, DomContent. Given a builder it will return a string.
public interface Renderable {
default <T extends Appendable> T render(HtmlBuilder<T> builder, Object model) throws IOException;
}
There are a few assumptions in this interface:
All the builders will be HTML builders
The parameter of the render method is the output type which is appendable (e.g. StringBuilder, StringWriter)
4. Recursive Type Bound
In a few places I see a parameterized class definitions like these:
public abstract class Tag<T extends Tag<T>> extends DomContent {
…
}
public class ContainerTag<T extends ContainerTag<T>> extends Tag<T> {
…
}
public class PTag extends ContainerTag<PTag> {
public PTag() {
super("p");
}
}
These kinds of definitions are useful to implement fluent APIs that are at the same time typesafe. Another advantage is that the autocomplete will show only the appropriate methods.
A good explanation of why this is useful is in this Stack Overflow article: https://stackoverflow.com/questions/7385949/what-does-recursive-type-bound-in-generics-mean
5. No Components
While j2html has many useful and interesting abstractions, it does not have a component model or abstractions to convert business objectives into HTML code. This has to be done by the application developer.