The asciidoctor-java-integration is the official means of using Asciidoctor to render all your AsciiDoc documentation using Java instead of Ruby.
Since asciidoctor-java-integration is a standard jar file, the only thing you should do is add library into classpath.
...
<dependencies>
<dependency>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-java-integration</artifactId>
<version>${asciidoctor.version}</version> <!--(1)-->
...
</dependency>
</dependencies>
...
...
dependencies {
compile('org.asciidoctor:asciidoctor-java-integration:${asciidoctor.version}') { //(1)
transitive = false
}
}
...
-
As this library tracks the version of asciidoctor, you can use whichever version of asciidoctor you prefer.
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.
Name | Description |
---|---|
|
Parse the AsciiDoc content into a Document and render it to the specified backend format. |
|
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.
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.
...
String rendered = asciidoctor.render("*This* is it.", Collections.EMPTY_MAP);
System.out.println(rendered);
...
But also you can render the content of 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:
...
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.
...
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.
...
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());
...
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.
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.
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:
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);
...
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);
...
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.
= 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...
//...
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)
-
prints Sample Document
-
prints Doc Writer
-
prints
doc.writer@asciidoc.org
-
prints Doc Writer
-
prints 2013-05-20
-
prints 1.0
-
prints First draft
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:
= 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.
for (ContentPart part : document.getParts()){
System.out.println(part.getTitle());
System.out.println("----");
System.out.println(part.getContent);
System.out.println("----");
}
= 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.
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.
[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:
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:
= 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:
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.
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);
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.
DirectoryWalker directoryWalker = new AsciiDocDirectoryWalker("target/test-classes/src");
List<File> asciidocFiles = directoryWalker.scan();
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.
Asciidoctor asciidoctor = create("my/gem/path");
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:
Flag | Value |
---|---|
|
RUBY1_9 |
|
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.
Flag | JDK |
---|---|
|
32 bits Java |
|
32/64 bits Java |
|
32/64 bits Java SE 7 |
|
32/64 bits Java SE 7 |
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.
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 :
-
<?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-Version: 1.0
Dependencies: org.asciidoctor
...
-
OR you can configure the dependency into the pom.xml thanks to the Maven JAR/WAR plugin
...
<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>
...
----
-
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
-
the module dependency will be added to the MANIFEST.MF file