AsciidoctorJ 1.6.0 released, at last!

by Robert Panzer, Dan Allen and Sarah White -

After several years of brewing (or perhaps barrel aging?), we have finally released AsciidoctorJ 1.6.0! The artifacts are available from Bintray and Maven Central.

What is AsciidoctorJ?

AsciidoctorJ is the official library for running Asciidoctor on the JVM. With AsciidoctorJ, you can convert AsciiDoc content, analyze the structure of a parsed AsciiDoc document, and write Asciidoctor extensions in Java and other JVM languages.

Before you upgrade, there are important things you need to know, which this post covers. While AsciidoctorJ is still built on the strong foundation of the Asciidoctor RubyGem, it has matured into a project in its own right. This release brings stability and key enhancements to new and existing users alike. Grab a glass so we can pour you all the details.

A major stepping-stone to SemVer

Despite its version number, this release should be considered a major release. As such, it’s not binary compatible with the 1.5.x releases.

AsciidoctorJ 1.6.0 is a final, transitional release in preparation for the switch to Semantic Versioning (SemVer). We’re breaking the rules one last time, but for good reason. Given how long the 1.6.0 branch has been barrel aging, we wanted to allow it to be tapped to gather feedback and tease out unexpected surprises before making the jump to 2.0.0 and SemVer. If all goes well, we expect the 2.0.0 release to follow shortly after this one. At that point, we’ll begin versioning AsciidoctorJ according to the SemVer rules.

To learn more about SemVer and how it affects the Asciidoctor projects in general, see Making the Switch to Semantic Versioning.

Switching to SemVer also means that AsciidoctorJ will now be versioned independently of Asciidoctor. AsciidoctorJ has evolved into much more than just the Asciidoctor RubyGem repackaged for the JVM. It has a complete API, including Java-based extensions and tight integration with the Java ecosystem (e.g., JUL, Ruby object bridging, etc). Its own independent version line will allow it to evolve more quickly and help everyone better understand what to expect in each release. Most important, it will address the very situation that backed up the 1.6.0 release for several years.

New features

Major API and packaging changes

Since the start of AsciidoctorJ, we’ve learned so much about JRuby and how it bridges Ruby and Java objects. Using that knowledge, we’ve completely redesigned the low-level integration to address shortcomings in the original design.

That redesign required us to make breaking changes to the API, which were absolutely necessary for AsciidoctorJ to continue to move forward. But it also provided us with the opportunity to fix numerous bugs that could not be fixed in the 1.5.x line without disrupting compatibility. You’ll be happy to know that the redesign eliminates glitches that may have bitten you when writing extensions or navigating the document model using the 1.5.x releases.

As part of this change, we split the API and implementation classes into separate packages and JARs to properly isolate them. We also reconciled the competing document models by mapping all the node types in Asciidoctor and removing the ContentPart and StructuredDocument APIs. Refer to the API docs (api, impl) to see what types are available and how they are organized.

Reworked extension API

Thanks to the redesign, the JRuby-based implementation is much better hidden behind the API. In particular, there are no more references in the API to the RubyObject class from JRuby.

The interfaces for the Abstract Syntax Tree (AST) were completely renamed to better represent their purpose. For example, the AbstractNode interface was renamed to ContentNode, AbstractBlock was renamed to StructuralNode, and Inline was renamed to PhraseNode. Even more of the AST has been mapped in Java, including list and definition lists, as well as helpers for creating a table structure in an extension processor.

Overall, the APIs for extensions and the AST should feel like a pure Java library (e.g., getAttr() is now getAttribute()). You can even define extension processors using annotation-based configuration (e.g., @Name).

Check out the integrator’s guide to learn all about it and find lots of examples.

Added converter API

A new converter API has been introduced, making it possible to implement a converter for a custom output format entirely in Java. This API allows you to extend Asciidoctor from Java, similar to what you could already do using the extension APIs. You can learn how to use this new API by following the converter documentation.

In fact, there’s already one converter that’s using this API from Groovy to convert AsciiDoc to Leanpub-flavored Markdown.

Access to Asciidoctor’s logs

Asciidoctor logs error and warning messages to the Ruby logger whenever it encounters a problem in the document. When integrating Asciidoctor into your own software, it’s important to capture these log messages in order to handle them appropriately. Previously, these messages were out of reach of AsciidoctorJ. That’s no longer the case. The new Log Handling API picks up messages logged in Asciidoctor and routes them to your own log handler.

Better documentation

AsciidoctorJ 1.6.0 offers extended and tested documentation for writing extension and converters. This documentation is guaranteed to be correct because it’s tested with every commit ;) And the process is underway to incorporate the documentation into the new Antora-based documentation site for Asciidoctor.

Refer to the release notes for a more detailed account of what changed in this release. Keep in mind that, since the development of 1.6.0 ran in parallel with 1.5.x for so long, many changes from 1.6.0 may already be familiar to you as they were backported into the 1.5.x release line.

Migration notes

If you only use the Asciidoctor, Asciidoctor.Factory, and *Builder classes (e.g., Asciidoctor#convertFile), you likely don’t have to change your code. In fact, the Asciidoctor Maven Plugin and the Asciidoctor Gradle Plugin already work with this release, primarily because they only rely on the public load/convert APIs.

The necessary changes to the AST and extension APIs did introduce breaking changes that require modifying your existing extensions. Migrating an extension is a matter of conforming to the new API. This mostly involves switching to the new names for AST interfaces and aligning with the updated method signatures. Aside from the name and signature changes, the API should still be recognizable and thus compatible with your existing logic.

Here are several examples of extensions written for both AsciidoctorJ 1.5.8 and 1.6.0 to give you an idea of the types of changes to look out for:

If you have difficulty migrating your extension or document introspection code, please file an issues in the issue tracker so we can find a path forward.

Java compatibility

Asciidoctor 1.6.0 requires at least Java 8. It also runs on, and is tested against, Java 11.

Starting in AsciidoctorJ 1.6.0, the project will align its Java compatibility with the official Oracle Java SE support roadmap. While AsciidoctorJ doesn’t require Oracle Java SE, that roadmap serves as a useful signpost for which versions of Java are considered current.

Asciidoctor 1.6.0 does not yet support running on the module path, a key feature of the Java Platform Module System introduced in Java 9. We’d like to get there in an upcoming version. That will depend, however, on the progress AsciidoctorJ’s dependencies make towards this goal, most notably JRuby. Despite this limitation, you can still run Asciidoctor 1.6.0 on Java 11.

Acknowledgements

We’d like to take this opportunity to name several key individuals who came together to make this release what it is today.

  • Jérémie Bresson for initiating the split between the API and implementation packages and modernizing the API signatures.

  • Abel Romero for his help with the design of the JUL logging integration and for testing it in the Maven plugin first.

  • Frank Becker for overhauling and streamlining the Gradle build.

  • Guillaume Grossetie for loads of cleanups and improvements across the API and implementation and for stress testing the redesign by launching AsciidoctorG.

  • Charles Nutter, Thomas Enebo, and the JRuby team for creating and maintaining JRuby, on which AsciidoctorJ is based.

  • And finally, our “elder”, Alex Soto for starting the AsciidoctorJ project and showing us a vision of what’s possible.

Of course, there are many more people to thank. As we’ve said many times, this project would not be possible without the incredible work and collaboration by the many volunteers who work on it. So, thank you!

Outlook for 2.0.0

The breaking changes aren’t over just yet. Heading towards 2.0.0, we want to further split the API of AsciidoctorJ and its implementation. Our big goal is to support alternative implementations underneath, such as Asciidoctor.js, using the same public API.

To help us get to 2.0.0, we ask that you test 1.6.0 and let us know if you run into any problems or changes that prevent you from migrating to it. Now’s the chance to get it right before the 2.0.0 release. Please file any issues you find in the issue tracker.

Thank you for coming on this journey with us as we work to bring the very best of AsciiDoc to the JVM.


Making the switch to Semantic Versioning

by Dan Allen -

After years of talking about it, we’re finally ready to make the switch to Semantic Versioning in Asciidoctor. This switch will happen as part of the next major release for each participating project.

About Semantic Versioning

Semantic Versioning (or SemVer, for short) is a logical system for selecting version numbers based on a simple MAJOR.MINOR.PATCH sequence.

  • A MAJOR version is for introducing incompatible changes (large or small).

  • A MINOR version is for adding backward-compatible functionality.

  • A PATCH version is for making bug fixes (backward compatible, of course).

There’s also room for a prerelease version via build metadata. You can learn more at https://semver.org.

In our current system, patch releases are indistinguishable from major and minor releases and there’s only a single release line. Experience has taught us (the hard way) that this situation is neither clear or sustainable. For us, SemVer is about having better communication and room to grow. It’s a system based on shared expectations and trust. Although it may not be perfect, SemVer should help to understand what a version number entails and when to upgrade. We can’t expect it to prevent breaking something, but at least you’ll know when it might.

Versioning projects independently

Switching to SemVer also means that the Asciidoctor projects, namely Asciidoctor, AsciidoctorJ, and Asciidoctor.js, will be versioned independently. Other projects in the ecosystem will likely start versioning independently as well (in fact, some already have).

AsciidoctorJ and Asciidoctor.js have both reached a point where the projects are much more than a redistribution of the Asciidoctor RubyGem. They have their own API, extensions model, custom features, and platform integration. And that warrants having their own version line. An independent version line will also allow users to better understand what to expect in each release.

After speaking with Robert (AsciidoctorJ lead) and Guillaume (Asciidoctor.js lead), here’s the system we agreed on:

  • The core processors (Asciidoctor, AsciidoctorJ, and Asciidoctor.js) will move to SemVer starting with the 2.0.0 release. We plan for all three projects to make this major version shift at roughly the same time to minimize confusion.

  • If Asciidoctor.js or AsciidoctorJ need a change in Asciidoctor, they can force Asciidoctor to be released. This means Asciidoctor will need to be “release ready” at all times. A patch or minor release won’t need much discussion, but a major release will, of course.

  • We will not attempt to align the version numbers between the three projects after 2.0.0. Guillaume laid out a strong case that trying to align the version numbers would work against SemVer and thus result in the version numbers losing their meaning. This does introduce the complexity of having to document the version of Asciidoctor that Asciidoctor.js or AsciidoctorJ provides. However, both AsciidoctorJ and Asciidoctor.js provide an API for accessing the version of the underlying Asciidoctor release. The release number is also available via the asciidoctor-version document attribute.

We considered ways to lock Asciidoctor, AsciidoctorJ, and Asciidoctor.js to the same version while still adhering to SemVer. However, we reached the conclusion that doing that would only perpetuate the situation that’s caused the backup of releases we’ve seen. It’s best if the projects have the freedom to release when the time is right to release. And with three version number segments (MAJOR.MINOR.PATCH), we finally have enough space for those releases to happen!

Next steps

We’ll start by repackaging the Asciidoctor 1.5.8 release as 2.0.0, with some minor adjustments to drop unsupported versions of Ruby. AsciidoctorJ and Asciidoctor.js will then follow with their 2.0.0 releases. At that point, we’ll stick to versioning according to SemVer rules as outlined above.


Use Arquillian and Docker to verify that AsciidoctorJ works in WildFly

by Maxime Gréau -

We’re excited to announce the newest member of the Asciidoctor family, Docker AsciidoctorJ. The Docker AsciidoctorJ project ensures that AsciidoctorJ can be used by any application deployed to a Java EE application server*.

* For now, only WildFly is tested. Pull requests welcome!

What’s it about?

This project provides:

A Dockerfile

Builds a Docker image that includes WildFly 8.2, AsciidoctorJ 1.5.2 and AsciidoctorJ PDF 1.5.0

Arquillian tests

Uses AsciidoctorJ inside a Docker container to convert AsciiDoc files to HTML and PDF files

AsciidoctorJ WildFly Docker containers managed by Arquillian
Figure 1. The power of Arquillian & Docker for integration testing AsciidoctorJ in WildFly

Learn more!

The following post explains how we execute Arquillian tests for AsciidoctorJ in a WildFly Docker container. We’ll talk about:

Want to know more? Read the full blog post to get all the details.


AsciidoctorJ 1.5.0 released!

by Alex Soto -

We’re thrilled to bring the latest Asciidoctor milestone, Asciidoctor 1.5.0, to the JVM as AsciidoctorJ 1.5.0!

What is AsciidoctorJ?

AsciidoctorJ is the official library for using Asciidoctor on the JVM. With AsciidoctorJ, you can convert AsciiDoc content or analyze the structure of a parsed AsciiDoc document from Java and other JVM languages.

Resolved Issues

The following issues have been resolved in version 1.5.0:

  • Added methods to register extensions to AsciidoctorJ which can be written in Ruby or Java. Resolves #90 and #157.

  • Creates an SPI so Java extensions can be registered automatically by simply adding the jar inside classpath. Resolves #97

  • Extension API is updated with modifications done in Asciidoctor 1.5.0. Resolves #113, #114, #148, #162 and #166.

  • Provides a method to unregister any extension previously registered. Resolves #122

  • Added -r and -I flags to CLI classes to require additional Ruby scripts and append to the load path, respectively. Resolves #171.

  • Added -V and --version flags to CLI classes. Resolves #87 and #117.

  • Adds integration with the Asciidoctor EPUB3 project. You can set epub3 as a backend. Resolves #168 and #179

  • Updates AbstractBlock class with findBy method. Resolves #164

  • Updates Document class to be aligned with Asciidoctor::Document so getting a title can return a Title class with title, subtitle instead of as String with full title. Resolves #167

  • Promotes attributes sectnumlevels, hardbreaks, appendix-caption, stem, hide-uri-schema, nofooter, source-language and compat-mode. Resolves #91, #92, #94, #105, #121, #129, #144 and #163.

  • You can get the Ruby instance used in AsciidoctorJ from JRubyRuntimeContext class. Resolves #93

  • Fixes a bug with Ruby instance and Attributes class which prevented the Gradle plugin from working properly. Resolves #96

  • Skips files and directories that begin with an underscore (_) in AsciiDocDirectoryWalker. Resolves #124

  • Adds slf4j as logging system. Resolves #126

  • Fixes a bug with base_dir and Ruby environment. Resolves #135

  • Document objects like Section, Block, Node, Document, …​ are moved to the org.asciidoctor.dom package.

  • Updates Java version to Java 7. Resolves #176

For more information about issues fixed in this release, please see the 1.5.0 milestone in the issue tracker!

Migration

The artifactId changed from asciidoctor-java-integration to asciidoctorj starting in 1.5.0. You should now use the following dependency stanza in your project’s pom.xml file:

<dependency>
    <groupId>org.asciidoctor</groupId>
    <artifactId>asciidoctorj</artifactId>
    <version>1.5.0</version>
</dependency>

When upgrading to Asciidoctor 1.5.0, please refer to the migration guide for details about how to migrate your content. We also encourage you to browse the release notes for Asciidoctor 1.5.0.

Asciidoctor 1.5.0 introduced many changes internally and to the public API. For example, the extension API has been modified in non-trivial ways, so you’ll like need to update your extensions to the new API when upgrading to this release of AsciidoctorJ.

Visit the updated AsciidoctorJ manual to learn how to install and use AsciidoctorJ.

This release makes way for new releases of the Gradle plugin, Maven plugin and Asciidoclet, all of which are based on AsciidoctorJ. Look for those announcements to follow!


  • 1 of 1