Revisiting Html in Java
Some time ago I wrote a post about creating an embedded dsl for Html in Java. Sadly, it was based on an abuse of lambda name reflection that was later removed from Java.
I thought I should do a followup because a lot of people still visit the old article. While it’s no longer possible to use lambda parameter names in this way, we can still get fairly close.
The following approach is slightly less concise. That said, it does have some benefits over the original:
a) You no longer need to have parameter name reflection enabled at compile time.
b) The compiler can check your attribute names are valid, and you can autocomplete them.
What does it look like?
html(
head(
title("Hello Html World"),
meta($ -> $.charset = "utf-8"),
link($->{ $.rel=stylesheet; $.type=css; $.href="/my.css"; }),
script($->{ $.type= javascript; $.src="//benjiweber.co.uk/some.js"; })
),
body(
div($-> $.cssClass = "article",
a($-> $.href="https://benjiweber.com/",
span($->$.cssClass="label", "Click Here"),
img($->{$.src="//benjiweber.co.uk/htmldsl2.png"; $.width=px(25); $.height=px(25); })
),
p(span("some text"), div("block"))
)
)
)
This generates the following html
Hello Html World
You get nice autocompletion, and feedback if you specify inappropriate values:
You’ll also get a helping hand from the types to not put tags in inappropriate places:

Generating Code
As it’s Java you can easily mix other code to generate markup dynamically:
assertEquals(
"""
Paragraph one
Paragraph two
Paragraph three
""".trim(),
html(
head(
meta($ -> $.charset = "utf-8")
),
body(
Stream.of("one","two","three")
.map(number -> "Paragraph " + number)
.map(content -> p(content))
)
).formatted()
);
And the code can help you avoid injection attacks by escaping literal values:
assertEquals(
"""
<script src="attack.js"></script>
""".trim(),
html(
head(
meta($-> $.charset = "utf-8")
),
body(
p("")
)
).formatted()
);
How does it work?
There’s only one “trick” here that’s particularly useful for DSLs. Using the Parameter Objects pattern from my lambda type references post.
The lambdas used for specifying the tag attributes are “aware” of their own types. And capable of instantiating the configuration they specify.
When we call
meta($ -> $.charset="utf-8")
We make a call to
default Meta meta(Parameters params, Tag... children) { … }
The lambda specifying the attribute config is structurally equivalent to the Parameters<Meta> type. This provides a get() function that instantiates an instance of Meta, and then passes the new instance to the lambda function to apply the config.
public interface Parameters extends NewableConsumer {
default T get() {
T t = newInstance();
accept(t);
return t;
}
}
Under the hood the newInstance() method uses reflection to examine the SerializedLambda contents and find the type parameter (in this case “Meta”) before instantiating it.
You can follow the code or see the previous post which explains it in a bit more detail.
Add Mixins
It’s helpful to use interfaces as mixins to avoid having to have one enormous class with all the builder definitions.
public interface HtmlDsl extends
Html.Dsl,
Head.Dsl,
Title.Dsl,
Meta.Dsl,
Link.Dsl,
Script.Dsl,
Body.Dsl,
Div.Dsl,
Span.Dsl,
A.Dsl,
P.Dsl,
Img.Dsl {}
Each tag definition then contains its own builder methods. We compose them together into a single HtmlDsl interface for convenience. This saves having to import hundreds of different methods. By implementing the Dsl interface a consumer gets access to all the builder methods.
Show me the code
It’s all on github. I’d start from the test examples. Bear in mind that it’s merely a port of the old proof of concept to a slightly different approach. I hope it helps illustrate the technique. It’s in no way attempting to be a complete implementation.
This approach can also be useful as an alternative to the builder pattern for passing a specification or configuration to a method. There’s another example on the type references article.
What else could you use this technique for?
The post Revisiting Html in Java appeared first on Benji's Blog.
Kraft Version 0.96
Ich freue mich, heute das Release Version 0.96 von Kraft herauszugeben. Die neue Version kann über die Homepage heruntergeladen werden.
Kraft 0.96 ist ein Bugfix Release, das einige kleine Fehler der Version 0.95 behebt. Insbesondere betrifft das das in 0.95 eingeführte neue PDF-Rendering mit Weasyprint.
Es gibt auch ein paar kleine neue Funktionen: Im Dokument Editor kann per Hinzufügen-Knopf nun auch eine neue Posten-Vorlage hinzugefügt werden. Außerdem werden in Folgedokumenten die im Vorgängerdokument eingetragenen Kopf- und Fußtexte kopiert, wenn die Standard-Texte für den neuen Dokumenttyp nicht gesetzt sind. Wichtig dabei: Kraft verwendet den Kopftext mit dem Namen „Standard“ als Standardtext, ebenso bei Fußtexten.
Weiterhin viel Erfolg mit Kraft!
Noodlings 23 | Not Fading Yet
Btrfs: Resolving the logical-resolve
openSUSE Tumbleweed – Review of the weeks 2021/08
Dear Tumbleweed users and hackers,
This week, we have released almost daily snapshots. It shows that I have received help in working on the Stagings. Richard has been very busy this week, working together with me on these areas. So, we managed to publish 6 snapshots (0218, 0219, 0220, 0221, 0222, and 0223).
The noteworthy changes therein were:
- Transactional-Updates 3.1.4
- GNOME 3.38.4
- binutils 2.36
- util-linux 2..36.2
- libguestfs 1.44.0
- PostgreSQL 13.2
- Mozilla Firefox 85.0.2
- Dracut 052
In the staging projects, we are preparing and testing these updates:
- NetworkManager 1.30
- Mozilla Firefox 86.0
- KDE Plasma 5.21.1
- podman 3.0.1
- Dracut 053
- Linux kernel 5.11.2
- openssl 1.1.1i, based on centralized crypto-policies package
- GCC 11 as default compiler
PostgreSQL, GNOME, Rubygems Update in Tumbleweed
Slonik fans are excited for this week’s openSUSE Tumbleweed snapshots as PostgreSQL has a major release in the rolling release distribution.
Snapshot 20210224 brought in the new postgresql 13 version. The new major version brings in highly requested features like parallelized vacuuming and incremental sorting. PostgreSQL brought some security enhancements with its extension system that allows developers to expand its functionality. There are also improvements to its indexing and lookup system, which benefit large databases. PostgreSQL wasn’t the only major version updated in the snapshot; the utility library ndctl jumped two versions to 70.1, which added firmware activation support. Other major version updates were made to liberation-fonts 2.1.1 and perl-Mail-DKIM 1.20200907. The Advanced Linux Sound Architecture package updated to version 1.2.4, which provided some plugin updates and Link Time Optimization fixes. Among other packages to update in the snapshot were bind 9.16.7, libsolv 0.7.16 and debugging tool xfsprogs 5.9.0.
An update of translations was made in the gnome-desktop 3.38.4 update within snapshot 20210222. A fix of a deadlock during startup was made in the Mozilla Firefox 85.0.2 update. The reading of multichannel PSD files with 1 or 2 channels were made in ImageMagick 7.0.11.0. The update of gtk3 3.24.25 fixed the touchscreen event handling and had some Wayland fixes for crashes on tablets; the update also added Application Programming Interfaces to support clients with subsurfaces better. Several new features were added with the firmware package fwupd 1.5.6; the updated added support for the System76 keyboard. Detecting the AMD Transparent Secure Memory Encryption encryption state for HSI-4 and new plugins were made available in the update.
The 20210220 snapshot could be dubbed the RubyGems snapshot. Rubygem-autoprefixer-rails 10.2.4.0, rubygem-bootsnap 1.7.2, rubygem-jbuilder 2.11.2, rubygem-msgpack 1.4.2, rubygem-puma 5.2.1 and rubygem-thor 1.1.0 were all updated in the snapshot. The rubygem-thor release added support for Ruby 3.0, and the rubygem-msgpack update dropped support of old Ruby versions that included 1.8, 1.9, 2.0, 2.1, 2.2 and 2.3.
The smallest snapshot this week with one package update was 20210219. The 1.44.0 version of libguestfs, which is a C library and a set of tools for accessing and modifying virtual disk images, requires a minimum version of Python 3.6. The update also removed references between openSUSE Leap and SUSE Linux Enterprise in the specfile.
Snapshot 20210218 started off the week and had several package updates for GNOME. Among the updates were personal information manager evolution 3.38.4, which fixed a memory leak when quoting headers for message replies, and gnome-maps 3.38.4, which fixed a drag hang and a bug that resulted in writing a broken last view position. A WebKitPluginProcess in webkit2gtk3 2.30.5 was returned to the package after mistakenly being removed. Other packages updated in the snapshot were freeipmi 1.6.7, glib2 2.66.7, udisks2 2.9.2, kplotting 5.79.0 and a new major version update of transactional-update 3.1.4, which fixed the syncing of SELinux attributes when using overlays.
about openQA-bites
openQA bites is a blog about tutorials, insights and love stories from a simple openQA developer. It aims to provide small and bite-sized posts about typical usage issues that every openQA dev encounters and atypical corner-cases that are worth to be written down. In the best case it can complement the official openQA documentation. by sharing some stories and make some typical and atypical use cases searchable in via your search engine of choice.
Active monitoring of openQA jobs
openqa-mon is a little command-line utility to monitor one or multiple openQA jobs for their status. This tool is useful if you want to live monitor a handful of jobs closely e.g. for verification runs.
Librsvg, Rust, and non-mainstream architectures
Almost five years ago librsvg introduced Rust into its source code. Around the same time, Linux distributions started shipping the first versions of Firefox that also required Rust. I unashamedly wanted to ride that wave: distros would have to integrate a new language in their build infrastructure, or they would be left without Firefox. I was hoping that having a working Rust toolchain would make it easier for the rustified librsvg to get into distros.
Two years after that, someone from Debian complained that this made it hard or impossible to build librsvg (and all the software that depends on it, which is A Lot) on all the architectures that Debian builds on — specifically, on things like HP PA-RISC or Alpha, which even Debian marks as "discontinued" now.
Recently there was a similar kerfuffle, this time from someone from Gentoo, specifically about how Python's cryptography package now requires Rust. So, it doesn't build for platforms that Rust/LLVM don't support, like hppa, alpha, and Itanium. It also doesn't build for platforms for which there are no Rust packages from Gentoo yet (mips, s390x, riscv among them).
Memories of discontinued architectures
Let me reminisce about a couple of discontinued architectures. If I'm reading Wikipedia correctly, the DEC Alpha ceased to be developed in 2001, and HP, who purchased Compaq, who purchased DEC, stopped selling Alpha systems in 2007. Notably, Compaq phased out the Alpha in favor of the Itanium, which stopped being developed in 2017.
I used an Alpha machine in 1997-1998, back at the University. Miguel kindly let me program and learn from him at the Institute where he worked, and the computer lab there got an Alpha box to let the scientists run mathematical models on a machine with really fast floating-point. This was a time when people actually regularly ssh'ed into machines to run X11 applications remotely — in their case, I think it was Matlab and Mathematica. Good times.
The Alpha had fast floating point, much faster than Intel x86 CPUs, and I was delighted to do graphics work on it. That was the first 64-bit machine I used, and it let me learn how to fix code that only assumed 32 bits. It had a really picky floating-point unit. Whereas x86 would happily throw you a NaN if you used uninitialized memory as floats, the Alpha would properly fault and crash the program. I fixed so many bugs thanks to that!
I also have fond memories of the 32-bit SPARC boxes at the University and their flat-screen fixed-frequency CRT displays, but you know, I haven't seen one of those machines since 1998. Because I was doing graphics work, I used the single SPARC machine in the computer lab at the Institute that had 24-bit graphics, with a humongous 21" CRT display. PCs at the time still had 8-bit video cards and shitty little monitors.
At about the same time that the Institute got its Alpha, it also got one of the first 64-bit UltraSPARCs from Sun — a very expensive machine definitely not targeted to hobbyists. I think it had two CPUs! Multicore did not exist!
I think I saw a single Itanium machine in my life, probably around 2002-2005. The Ximian/Novell office in Mexico City got one, for QA purposes — an incredibly loud and unstable machine. I don't think we ever did any actual development on that box; it was a "can you reproduce this bug there" kind of thing. I think Ximian/Novell had a contract with HP to test the distro there, I don't remember.
Unsupported architectures at the LLVM level
Platforms like the Alpha and Itanium that Rust/LLVM don't support — those platforms are dead in the water. The compiler cannot target them, as Rust generates machine code via LLVM, and LLVM doesn't support them.
I don't know why distributions maintained by volunteers give themselves the responsibility to keep their software running on platforms that have not been manufactured for years, and that were never even hobbyist machines.
I read the other day, and now I regret not keeping the link, something like this: don't assume that your hobby computing entitles you to free labor on the part of compiler writers, software maintainers, and distro volunteers. (If someone helps me find the source, I'll happily link to it and quote it properly.)
Non-tier-1 platforms and "$distro does not build Rust there yet"
I think people are discovering these once again:
-
Writing and supporting a compiler for a certain architecture takes Real Work.
-
Supporting a distro for a certain architecture takes Real Work.
-
Fixing software to work on a certain architecture takes Real Work.
Rust divides its support for different platforms into tiers, going from tier 1, the most supported, to tier 3, the least supported. Or, I should say, taken care of, which is a combination of people who actually have the hardware in question, and whether the general CI and build tooling is prepared to deal with them as effectively as it does for tier 1 platforms.
In other words: there are more people capable of paying attention to, and
testing things on, x86_64 PCs than there are for
sparc-unknown-linux-gnu.
Some anecdotes from Suse
At Suse we actually support IBM's s390x big iron; those mainframes run Suse Linux Enterprise Server. You have to pay a lot of money to get a machine like that and support for it. It's a room-sized beast that requires professional babysitting.
When librsvg and Firefox started getting rustified, there was of course concern about getting Rust to work properly on the s390x. I worked sporadically with the people who made the distro work there, and who had to deal with building Rust and Firefox on it (librsvg was a non-issue after getting Rust and Firefox to work).
I think all the LLVM work for the s390x was done at IBM. There were probably a couple of miscompilations that affected Firefox; they got fixed.
One would expect bugs in software for IBM mainframes to be fixed by IBM or its contractors, not by volunteers maintaining a distro in their spare time.
Giving computing time on mainframes to volunteers in distros could seem like a good samaritan move, or a trap to extract free labor from unsuspecting people.
Endianness bugs
Firefox's problems on the s390x were more around big-endian bugs than anything. You see, all the common architectures these days (x86_64 and arm64) are little-endian. However, s390x is big-endian, which means that all multi-byte numbers in memory are stored backwards from what most software expects.
It is not a problem to write software that assumes little-endian or big-endian all the time, but it takes a little care to write software that works on either.
Most of the software that volunteers and paid people write assumes little-endian CPUs, because that is likely what they are targeting. It is a pain in the ass to encounter code that works incorrectly on big-endian — a pain because knowing where to look for evidence of bugs is tricky, and fixing existing code to work with either endianness can be either very simple, or a major adventure in refactoring and testing.
Two cases in point:
Firefox. When Suse started dealing with Rust and Firefox in the s390x, there were endianness bugs in the graphics code in Firefox that deals with pixel formats. Whether pixels get stored in memory as ARGB/ABGR/RGBA/etc. is a platform-specific thing, and is generally a combination of the graphics hardware for that platform, plus the actual CPU architecture. At that time, it looked like the C++ code in Firefox that deals with pixels had been rewritten/refactored, and had lost big-endian support along the way. I don't know the current status (not a single big-endian CPU in my vincinity), but I haven't seen related bugs come in the Suse bug tracker? Maybe it's fixed now?
Librsvg had two root causes of bugs for
big-endian. One was in the old code for SVG
filter effects that was written in C; it never supported big-endian. The
initial port to Rust inherited the same bug (think of a line-by-line
port, althought it wasn't exactly like that), but it got fixed when my
Summer of Code intern Ivan Molodetskikh refactored the code to have a
Pixel abstraction that works for little-endian and big-endian, and
wraps Cairo's funky requirements.
The other endian-related bug in librsvg was when computing
masks. Again, a little refactoring with that Pixel
abstraction fixed it.
I knew that the original C code for SVG filter effects didn't work on
big-endian. But even back then, at Suse we never got
reports of it producing incorrect results on the s390x... maybe people don't use
their mainframes to run rsvg-convert? I was hoping that the port to
Rust of that code would automatically fix that bug, and it kind of
happened that way through Ivan's careful work.
And the code for masks? There were two bugs reported with that same root cause: one from Debian as a failure in librsvg's test suite (yay, it caught that bug!), and one from someone running an Apple PowerBook G4 with a MATE desktop and seeing incorrectly-rendered SVG icons.
And you know what? I am delighted to see people trying to keep those lovely machines alive. A laptop that doesn't get warm enough to burn your thighs, what a concept. A perfectly serviceable 32-bit laptop with a maximum of about 1 GB of RAM and a 40 GB hard drive (it didn't have HDMI!)... But you know, it's the same kind of delight I feel when people talk about doing film photography on a Rollei 35. A lot of nostalgia for hardware of days past, and a lot of mixed feelings about not throwing out working things and creating more trash.
As a graphics programmer I feel the responsibility to write code that works on little-endian and big-endian, but you know, it's not exactly an everyday concern anymore. The last big-endian machine I used on an everyday basis was the SPARCs in the university, more than 20 years ago.
Who gets paid to fix this?
That's the question. Suse got paid to support Firefox on the s390x; I suppose IBM has an interest in fixing LLVM there; both actually have people and hardware and money to that effect.
Within Suse, I am by default responsible for keeping librsvg working for the s390x as well — it gets built as part of the distro, after all. I have never gotten an endianness bug report from the Suse side of things.
Which leads me to suspect that, probably similar to Debian and Gentoo, we build a lot of software because it's in the build chain, but we don't run it to its fullest extent. Do people run GNOME desktops on s390x virtual machines? Maybe? Did they not notice endianness bugs because they were not in the code path that most GNOME icons actually use? Who knows!
I'm thankful to Simon from the Debian bug for pointing out the failure in librsvg's test case for masks, and to Mingcong for actually showing a screenshot of a MATE desktop running on a PPC PowerBook. Those were useful things for them to do.
Also — they were kind about it. It was a pleasure to interact with them.
Tour Sketches
One of the things that came up during user testing GNOME 40 was that people went through the Tour and all the abstract(ish) UI illustrations didn't really help people orient themselves on screen. We did get positive feedback on the feel of the tour though. For the new Tour we iterated on some illustrations that help solidify the style and give a nice welcoming vibe to the experience.
Because you can soon experience GNOME 40 yourself, here's a few sketches of the exploratory phase. Be sure to check out some background on how the animations were done.







