Text mit Extras: Asciidoctor auf Tomcat nutzen

Seite 2: Erstellen des Servlet

Inhaltsverzeichnis

Wenn Entwickler Java-Servlets an eine Dateiendung binden, führt der Server ihren Code automatisch beim Aufruf einer Ressource mit der passenden Endung aus. Für den Beispielcode soll ein Servlet beim Aufruf einer Datei mit der Endung .adoc starten.

Das Servlet kann anschließend anhand des Uniform Resource Locator (URL) aus dem HTTP-Request den Ablageort der Datei bestimmen. Anschließend prüft es, ob sich dort eine gleichnamige HTML- beziehungsweise PDF-Datei befindet. Wenn letztere fehlt oder älter ist als die .adoc-Datei, transformiert es die .adoc-Datei mit Asciidoctor in eine HTML- oder PDF-Datei und gibt das Ergebnis aus.

Die einzelnen Schritte lassen sich jeweils mit ein wenig Java-Code bewältigen, und das fertige Servlet mit dem Namen AdocServlet.java sieht folgendermaßen aus:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.HashMap;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

import static org.asciidoctor.Asciidoctor.Factory.create;
import org.asciidoctor.Asciidoctor;

/**
 * Das AdocServlet wandelt AsciiDoc-Inhalte (*.adoc)
 * zu HTML-Seiten und PDF-Dokumenten
 *
 * Mit Angabe des Parameters ?pdf=true in der URL 
 * wird PDF erzeugt, andernfalls HTML
 */
public class AdocServlet extends HttpServlet  {

  private static final String DOT = ".";
  private static final String HTML = "html";
  private static final String PDF = "pdf";
  private static final String SERVLET_NAME = "AdocServlet";

  /**
   * Die Methode processRequest verarbeitet 
   * HTTP-Anfragen des Typs
   * <code>GET</code> und <code>POST</code>.
   *
   * @param request die Servlet-Anfrage
   * @param response die Servlet-Antwort
   * @throws ServletException wenn ein 
   * Servlet-spezifischer Fehler passiert
   * @throws IOException wenn ein Eingabe- 
   * oder Ausgabe-Fehler passiert
   */
  protected void 
    processRequest(HttpServletRequest request,
                   HttpServletResponse response)
          throws ServletException, IOException
  {

    // Asciidoc-Quelldatei aus HTTP-Request ermitteln
    String vPath = request.getServletPath();
    String absname = getServletContext().getRealPath(vPath);
    File adocfile = new File(absname);

    // HTML-Datei ermitteln
    String nameext = adocfile.getName();
    String fname = 
      nameext.substring(0, nameext.lastIndexOf(DOT));
    File htmlfile = 
      new File(adocfile.getParentFile(), 
               fname + DOT + HTML);
    File outfile = htmlfile; // Standardmaessig HTML
    response.setContentType("text/html;charset=UTF-8");

    /*
     * nach HTML transformieren, wenn die 
     * Quelle sich geandert hat oder
     * die HTML-Datei noch nicht existiert
    */
    if(!htmlfile.exists() || 
       adocfile.lastModified() > htmlfile.lastModified())
    {
      transform(absname);
    }

    /*
     *  nach PDF transformieren, wenn der
     *  Parameter pdf=true existiert und
     *  wenn die Quelle sich geandert hat oder
     * die PDF-Datei noch nicht existiert
    */
    String pdf = request.getParameter(PDF);
    if(null != pdf && 
      pdf.equalsIgnoreCase(Boolean.TRUE.toString())) 
    {
      File pdffile = 
        new File(adocfile.getParentFile(), 
                 fname + DOT + PDF);
      outfile = pdffile; // PDF zurueckgegeben
      response.setContentType(
        "application/pdf;charset=UTF-8");
      if(!pdffile.exists() || adocfile.lastModified() 
         > pdffile.lastModified()) 
      {
        transform(absname, PDF);
      }
    }

    try (PrintWriter out = response.getWriter()) {
      // abhaengig vom Parameter pdf 
      // HTML- oder PDF-Datei ausgeben
      FileInputStream in = new FileInputStream(outfile);
      BufferedReader reader = 
        new BufferedReader(new InputStreamReader(in));
      String line;
      while ((line = reader.readLine()) != null) {
        out.println(line);
      }
    }
  }

  /**
   * Nach HTML transformieren
   * @param fileName der Dateiname der 
   * Quelldatei samt absoluter Pfadangabe
   */
  private void transform(String fileName) {
    transform(fileName, null);
  }

  /**
   * In ein Format transformieren, das von 
   * einem 'Backend' von Asciidoctor
   * unterstuetzt wird
   * @param fileName der Dateiname der Quelldatei 
   * samt absoluter Pfadangabe
   * @param backend das Kuerzel des Backends, 
   * z.B. der String 'pdf', wenn
   * nach PDF transformiert werden soll
   */
  private void transform(String fileName, String backend) {
    Map<String, Object> attributes = new HashMap<>();
    attributes.put("no_footer", false);
    attributes.put("source_highlighter", "highlightjs");

    Map<String, Object> options = new HashMap<>();
    options.put("attributes", attributes);
    options.put("in_place", false);
    if(null != backend) {
      options.put("backend", backend);
    }

    Asciidoctor asciidoctor = create();
    asciidoctor.convertFile(new File(fileName), options);
  }

  /**
   * Die HTTP-<code>GET</code>-Methode verarbeiten.
   *
   * @param request die Servlet-Anfrage
   * @param response die Servlet-Antwort
   * @throws ServletException wenn ein 
   * Servlet-spezifischer Fehler passiert
   * @throws IOException wenn ein Eingabe- 
   * oder Ausgabe-Fehler passiert
   */
  @Override
  protected void 
    doGet(HttpServletRequest request, 
          HttpServletResponse response)
          throws ServletException, IOException 
  {
    processRequest(request, response);
  }

  /**
   * Die HTTP-<code>POST</code>-Methode verarbeiten.
   *
   * @param request die Servlet-Anfrage
   * @param response die Servlet-Antwort
   * @throws ServletException wenn ein 
   * Servlet-spezifischer Fehler passiert
   * @throws IOException wenn ein Eingabe- 
   * oder Ausgabe-Fehler passiert
   */
  @Override
  protected void 
    doPost(HttpServletRequest request, 
           HttpServletResponse response)
          throws ServletException, IOException 
  {
    processRequest(request, response);
  }

  /**
   * Eine Kurzbeschreibung des Servlets ausgeben.
   *
   * @return einen String mit der Kurzbeschreibung
   * des Servlets
   */
  @Override
  public String getServletInfo() {
    return SERVLET_NAME;
  }
}

Die Hauptarbeit der Transformation ist in den Klassenbibliotheken von AsciidoctorJ enthalten. Der Aufruf von asciidoctor.convertFile genügt für den Anwendungsfall, und das Servlet ergänzt es lediglich um Funktionen zum Bestimmen der gewünschten Datei und der Entscheidung, ob die Transformation erforderlich ist.

Die aus dem Quellcode kompilierte Servlet-Klasse sollte ebenfalls im Ordner CATALINA_BASE/lib von Tomcat liegen. Damit ist das Servlet von allen Webanwendungen des Servers verwendbar. Alternativ lässt es sich im Verzeichnis WEB-INF/lib der einzelnen Webanwendungen (Web-Archiv, .war-Datei) verteilen, die es benutzen sollen.

Asciidoctor hält für die Umwandlung nach PDF einen direkten Ansatz bereit, der ohne Umwege über Drittformate auskommt. Der Aufruf ist analog zu dem für HTML und erfordert bei den Optionen lediglich ein anderes Backend.