Skip to content

Asciidoctor Java integration & API - Welcomes Asciidoctor to the JVM!

License

Notifications You must be signed in to change notification settings

mtolk/asciidoctorj

 
 

Repository files navigation

AsciidoctorJ: Asciidoctor Java integration & API

Build Status

The asciidoctor-java-integration is the official means of using Asciidoctor to render all your AsciiDoc documentation using Java instead of Ruby.

Installation

Since asciidoctor-java-integration is a standard jar file, the only thing you should do is add library into classpath.

Dependency declaration in Maven
...
<dependencies>
  <dependency>
    <groupId>org.asciidoctor</groupId>
    <artifactId>asciidoctor-java-integration</artifactId>
    <version>${asciidoctor.version}</version>  <!--(1)-->
    ...
  </dependency>
</dependencies>
...
Dependency declaration in Gradle
...
dependencies {
    compile('org.asciidoctor:asciidoctor-java-integration:${asciidoctor.version}') {  //(1)
        transitive = false
    }
}
...
  1. As this library tracks the version of asciidoctor, you can use whichever version of asciidoctor you prefer.

Usage

The core interface of asciidoctor-java-integration is Asciidoctor interface. It provides two methods for rendering asciidoc content, render and renderFile. Both of them returns a string with rendered content.

Table 1. Method description
Name Description

render

Parse the AsciiDoc content into a Document and render it to the specified backend format.

renderFile

Parse the content of AsciiDoc file into a Document and render it to the specified backend format.

Also a factory method is provided to create an instance of Asciidoctor interface.

Creation of Asciidoctor interface
import static org.asciidoctor.Asciidoctor.Factory.create;
import org.asciidoctor.Asciidoctor;
...
Asciidoctor asciidoctor = create();
...

And then we can call render methods depending on our requirements.

Rendering a String
...
String rendered = asciidoctor.render("*This* is it.", Collections.EMPTY_MAP);
System.out.println(rendered);
...

But also you can render the content of a file.

Rendering a File
...
String rendered = asciidoctor.renderFile(new File("target/test-classes/rendersample.asciidoc"), Collections.EMPTY_MAP);
System.out.println(rendered);
...

Or a list of Asciidoc files:

Rendering a list of files.
...
String[] allRenderedFiles = asciidoctor.renderFiles(Arrays.asList(new File("target/test-classes/rendersample.asciidoc")), options);
...

If the rendered content is not written into files, renderDirectory will return an array listing all the documents rendered.

Another method provided by Asciidoctor interface is renderDirectory. This method renders all AsciiDoc files (_.asc_, _.asciidoc_, _.ad_ or _.adoc_), that are present inside provided folder or any of its subfolder.

In case rendered content is not written in files, this method returns an array with all documents rendered.

Rendering all files of directory
...
String[] allRenderedFiles = asciidoctor.renderDirectory(new File("target/test-classes/src"), new HashMap<String, Object>());

for(String renderedFile:allRenderedFiles) {
    System.out.println(renderedFile);
}
...

Another way to render AsciiDoc content is by calling render method but providing a Reader and Writer. Reader interface is used as source, and rendered content is written through Writer interface.

Rendering content to a Writer
...
FileReader inputAsciidoctorFile = new FileReader(new File("target/test-classes/rendersample.asciidoc"));
StringWriter rendererWriter = new StringWriter();

asciidoctor.render(inputAsciidoctorFile, rendererWriter, options().asMap());

StringBuffer renderedContent = rendererWriter.getBuffer();
assertRenderedFile(renderedContent.toString());
...

Options

Asciidoctor supports different kind of options, like in_place which renders the output inside a file, template_dir used to provide a directory of Tilt-compatible templates to be used instead of the default built-in templates, or for example attributes option where we can set key-value pairs of attributes that will be used within asciidoc document.

The second parameter of render methods are a java.util.Map where all these options can be set.

Example of using in_place Option and backend Attribute
Map<String, Object> attributes = new HashMap<String, Object>();
attributes.put("backend", "docbook");

Map<String, Object> options = new HashMap<String, Object>();
options.put("in_place", true);
options.put("attributes", attributes);

String render = asciidoctor.renderFile("target/test-classes/rendersample.asciidoc", options);

See that in previous example we have created a Map, where we have put the options and attributes (creating a Map too) required to render input as docbook and generate an output file.

Another way for setting options is by using org.asciidoctor.Options class. Options is a simple Java class which contains methods for setting required options. Note that related with org.asciidoctor.Options class, there is org.asciidoctor.Attributes class, which can be used for setting attributes.

render methods are overloaded, so we can pass org.asciidoctor.Options as parameter instead of java.util.Map.

Example of using Options and Attributes class instead of Map.
Attributes attributes = new Attributes();
attributes.setBackend("docbook");

Options options = new Options();
options.setInPlace(true);
options.setAttributes(attributes);

String render = asciidoctor.renderFile("target/test-classes/rendersample.asciidoc", options);

...

But asciidoctor-java-integration also provides two fluent interfaces to create these maps and classes in a more readable form.

org.asciidoctor.AttributesBuilder is provided for creating required attributes set, and org.asciidoctor.OptionsBuilder can be used for options. Previous example but using these classes looks like:

Example setting attributes and options with map
import static org.asciidoctor.AttributesBuilder.attributes;
import static org.asciidoctor.OptionsBuilder.options;

...

Map<String, Object> attributes = attributes().backend("docbook").asMap();
Map<String, Object> options = options().inPlace(true).attributes(attributes).asMap();

String render = asciidoctor.renderFile("target/test-classes/rendersample.asciidoc", options);

...
Example setting attributes and options with classes
import static org.asciidoctor.AttributesBuilder.attributes;
import static org.asciidoctor.OptionsBuilder.options;

...

Attributes attributes = attributes().backend("docbook").get();
Options options = options().inPlace(true).attributes(attributes).get();

String render = asciidoctor.renderFile("target/test-classes/rendersample.asciidoc", options);

...

Document Header

readDocumentHeader retrieve information from the header of an AsciiDoc document without parsing or rendering the entire document. This method returns an instance of org.asciidoctor.DocumentHeader with all information from the header filled.

AsciiDoc document with header information
= Sample Document
Doc Writer <doc.writer@asciidoc.org>; John Smith <john.smith@asciidoc.org>
v1.0, 2013-05-20: First draft
:title: Sample Document
:tags: [document, example]

Preamble...
Retrieving some of header information
//...
DocumentHeader header = asciidoctor.readDocumentHeader(new File("target/test-classes/documentheaders.asciidoc"));

System.out.println(header.getDocumentTitle()); //(1)

Author author = header.getAuthor();            //(2)
System.out.println(author.getEmail());         //(3)
System.out.println(author.getFullName());      //(4)

RevisionInfo revisionInfo = header.getRevisionInfo();

System.out.println(revisionInfo.getDate());    //(5)
System.out.println(revisionInfo.getNumber());  //(6)
System.out.println(revisionInfo.getRemark());  //(7)
  1. prints Sample Document

  2. prints Doc Writer

  3. prints doc.writer@asciidoc.org

  4. prints Doc Writer

  5. prints 2013-05-20

  6. prints 1.0

  7. prints First draft

Document structure

readDocumentStructure provides easy and useful way of parsing asciidoc file into the structured object. First of all it gathers exactly the same information as readDocumentHeader and puts it in header filed of StructuredDocument object. Actual content of the file is split into separate ContentParts based on blocks of the content.

There are few possible use cases of using this feature, please consider following examples:

AsciiDoc document with two blocks defined by section titles
= Sample Document

== Section one
This is content of section one

== Section two
And content of section two

...

Each section defines new content part. List of all parts can be get by getParts method on StructuredDocument. Each part will than contain of title (ie. "Section one") and rendered text content as html.

Print content of each part
for (ContentPart part : document.getParts()){
    System.out.println(part.getTitle());
    System.out.println("----");
    System.out.println(part.getContent);
    System.out.println("----");
}
AsciiDoc document with two blocks defined by styles
= Sample Document

[style one]
This is content of first content part

[[partId]]
[style two,role=partRole]
--
And content of second content part

This block can be as long as you want.
--

This way you can then use methods like getPartByStyle to retrieve particular content parts.

Retrieve content part by style
ContentPart style_two = document.getPartByStyle("style two");
// other possible way of retrieving parts:
ContentPart style_two = document.getPartById("partId")
ContentPart style_two = document.getPartByRole("partRole")

//and also for lists
List<ContentPart> parts = document.getPartsByStyle("style two");
List<ContentPart> parts = document.getPartsByRole("partRole");
List<ContentPart> parts = document.getPartsByContext("open");

Really nice thing about it is possibility to parse images to Image object that you can use later to embed in html page directly from your java code or manipulate in any other way.

Define images
[Images]
image::src/some{sp}image{sp}1.JPG[TODO title1,link="link1.html"]
image::src/some{sp}image{sp}2.JPG[TODO title2,link="link2.html"]

to get a list of images defined in the document and then to process images:

Retrieve image information
List<ContentPart> images = document.getPartsByContext("image");
for (ContentPart image : images){
    String src = (String) image.getAttributes().get("target");
    String alt = (String) image.getAttributes().get("alt");
    String link = (String) image.getAttributes().get("link");
}

As of final example consider following complete use case:

AsciiDoc document with product definition
= Sample product
v1.0, 2013-10-12
:hardbreaks:

:price: 70 pln
:smallImage: photos/small/small_image.jpg

[Description]
short product description

[Images]
image::photos/image1.jpg[title]
image::photos/image2.jpg[title]


[Detail]
--
Detail information about product. Note that you can use all asciidoc features here like:
.simple list
* lists
* images
* titles
* further blocks

[role=text-center]
also you can also add css style by assigning role to the text.
--

and the way it can be than transformed to java object:

Java method for getting product
Product product = new Product();
product.setTitle(document.getHeader().getDocumentTitle());
product.setPrice(new Price((String) document.getHeader().getAttributes().get("price")));
product.setSmallImage(new Image((String)document.getHeader().getAttributes().get("smallImage"),product.getTitle()));

product.setDescription(document.getPartByStyle("description").getContent());

List<ContentPart> images = document.getPartsByContext("image");
for (ContentPart image : images) {
    Image image = new Image();
    image.setSrc((String) image.getAttributes().get("target"));
    image.setAlt((String) image.getAttributes().get("alt"));
    product.getImages().add(image);
}

product.setDetail(document.getPartByStyle("detail").getContent());

Last feature of structure document is possibility to configure how deeply should blocks be processed. Default is one level only so if you want to have more nested structure add STRUCTURE_MAX_LEVEL parameter to processing options.

Configuration of the structure document processing
Map<String,Object> parameters = new HashMap<String, Object>();
parameters.put(Asciidoctor.STRUCTURE_MAX_LEVEL, 2);
StructuredDocument document = asciidoctor.readDocumentStructure(
    new File("target/test-classes/documentblocks.asciidoc"),
    parameters);

Utilities

A utility class for searching all asciidoc files present in a root folder and all its subfolders is given. In fact it finds all files that end up with .asc, .asciidoc, .ad or .adoc. This class is AsciiDocDirectoryWalker.

Example of finding all asciidoc
DirectoryWalker directoryWalker = new AsciiDocDirectoryWalker("target/test-classes/src");
List<File> asciidocFiles = directoryWalker.scan();

GEM_PATH

By default asciidoctor-java-integration comes with all required gems bundled within the jar. But in some circumstances like OSGi environments you may require to store gems in an external directory and be loaded by asciidoctor-java-integration. To accomplish this scenario, create method provides a parameter to set it.

Example of setting GEM_PATH
Asciidoctor asciidoctor = create("my/gem/path");

Optimization

Sometimes JRuby starting time is slower than we would expect if we were using standard C-based, non-optimizing standard Ruby. For improving this time, JRuby offers some flags which can be used to tune JRuby applications. Apart of these flags, or in conjunction with them, we can use some java flags to improve even more the startup time.

For small tasks such as converting an AsciiDoc document, there are two JRuby flags can improve the startup time:

Table 2. JRuby flags
Flag Value

jruby.compat.version

RUBY1_9

jruby.compile.mode

OFF

Both flags are set by default inside asciidoctor-java-integration project, so we do not have to worry about setting them manually.

As mentioned before, there are some Java flags that can also be used for this purpose. These flags depends on version of JDK and also if you are working on 32/64 bits version. These flags can be set by using JRUBY_OPTS environment variable. Let’s see a summary of these flags and in which versions can be used.

Table 3. Java flags
Flag JDK

-client

32 bits Java

-Xverify:none

32/64 bits Java

-XX:+TieredCompilation

32/64 bits Java SE 7

-XX:TieredStopAtLevel=1

32/64 bits Java SE 7

Setting flags for Java SE 6
export JRUBY_OPTS="-J-Xverify:none -J-client"

Note that you should add -J before the flag.

You can find a full explanation on how to improve startup time of JRuby applications at Improving Startup Time.

Running on WildFly AS

If you want to use Asciidoctor-java-integration in your application deployed on WilFly AS, you have to follow the instruction below :

  • Create a Asciidoctor module for WildFly AS

    • create the following folder tree : $JBOSS_HOME/modules/org/asciidoctor/main

    • create the module descriptor file module.xml like this :

Asciidoctor module descriptor for WildFly AS
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.0" name="org.asciidoctor">
  <resources>
    <resource-root path="asciidoctor-java-integration-0.1.4.jar"/>
    <resource-root path="jcommander-1.30.jar"/>
    <resource-root path="jruby-complete-1.7.4.jar"/>
  </resources>

  <dependencies>
    <module name="javax.management.j2ee.api"/>
    <module name="javax.api"/>
  </dependencies>
</module>
  • Add a dependency on your Java archive to this WildFly module choosing between 2 options :

    • you can either add the dependency just into the MANIFEST.MF file

MANIFEST.MF file example with dependency to Asciidoctor module
Manifest-Version: 1.0
Dependencies: org.asciidoctor
...
  • OR you can configure the dependency into the pom.xml thanks to the Maven JAR/WAR plugin

pom.xml file example with Maven WAR plugin configuration to add a dependency
...
<dependencies>
  <dependency>
    <groupId>org.asciidoctor</groupId>
    <artifactId>asciidoctor-java-integration</artifactId>
    <version>${asciidoctor.version}</version>
    <scope>provided</scope>                               (1)
    ...
  </dependency>
</dependencies>
...
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <version>${maven.war.plugin.version}</version>
    <configuration>
      <archive>
        <manifestEntries>
          <Dependencies>org.asciidoctor</Dependencies>   (2)
        </manifestEntries>
      </archive>
    </configuration>
</plugin>
...
----
  1. asciidoctor-java-version dependency and all these transitives dependencies don’t need to be added to the final WAR since all JARs are available through the module

  2. the module dependency will be added to the MANIFEST.MF file

About

Asciidoctor Java integration & API - Welcomes Asciidoctor to the JVM!

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Java 98.6%
  • Other 1.4%