All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jetty.docs.JettyIncludeExtension Maven / Gradle / Ivy

//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.docs;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.asciidoctor.Asciidoctor;
import org.asciidoctor.ast.Document;
import org.asciidoctor.extension.IncludeProcessor;
import org.asciidoctor.extension.PreprocessorReader;
import org.asciidoctor.jruby.extension.spi.ExtensionRegistry;
import org.eclipse.jetty.tests.hometester.JettyHomeTester;

/**
 * 

Asciidoctor include extension that includes into * the document the output produced by starting a Jetty server.

*

Example usage in an Asciidoc page:

*
 * include::jetty[setupArgs="--add-modules=http,deploy,demo-simple",highlight="WebAppContext"]
 * 
*

Available configuration parameters are:

*
*
setupModules
*
Optional, specifies a comma-separated list of files to copy to {@code $JETTY_BASE/modules}.
*
setupArgs
*
Optional, specifies the arguments to use in a Jetty server setup run. * If missing, no Jetty server setup run will be executed. * The output produced by this run is ignored.
*
args
*
Optional, specifies the arguments to use in a Jetty server run. * If missing, a Jetty server run will be executed with no arguments. * The output produced by this run is included in the Asciidoc document.
*
replace
*
Optional, specifies a comma-separated pair where the first element is a regular * expression and the second is the string replacement.
*
delete
*
Optional, specifies a regular expression that when matched deletes the line
*
highlight
*
Optional, specifies a regular expression that matches lines that should be highlighted. * If missing, no line will be highlighted. * If the regular expression contains capturing groups, only the text matching * the groups is highlighted, not the whole line. *
*
callouts
*
Optional, specifies a comma-separated pair where the first element is a callout * pattern, and the second element is a comma-separated list of regular expressions, * each matching a single line, that get a callout added at the end of the line.
*
* * @see JettyHomeTester */ public class JettyIncludeExtension implements ExtensionRegistry { public void register(Asciidoctor asciidoctor) { asciidoctor.javaExtensionRegistry().includeProcessor(JettyIncludeProcessor.class); } public static class JettyIncludeProcessor extends IncludeProcessor { @Override public boolean handles(String target) { return "jetty".equals(target); } @Override public void process(Document document, PreprocessorReader reader, String target, Map attributes) { try { // Document attributes are converted by Asciidoctor to lowercase. Path jettyDocsPath = Path.of((String)document.getAttribute("project-basedir")); Path jettyHome = jettyDocsPath.resolve("../../jetty-home/target/jetty-home").normalize(); JettyHomeTester jetty = JettyHomeTester.Builder.newInstance() .jettyHome(jettyHome) .mavenLocalRepository((String)document.getAttribute("maven-local-repo")) .build(); String setupModules = (String)attributes.get("setupModules"); if (setupModules != null) { Path jettyBaseModules = jetty.getJettyBase().resolve("modules"); Files.createDirectories(jettyBaseModules); String[] modules = setupModules.split(","); for (String module : modules) { Path sourcePath = jettyDocsPath.resolve(module.trim()); Files.copy(sourcePath, jettyBaseModules.resolve(sourcePath.getFileName())); } } String setupArgs = (String)attributes.get("setupArgs"); if (setupArgs != null) { try (JettyHomeTester.Run setupRun = jetty.start(setupArgs.split(" "))) { setupRun.awaitFor(15, TimeUnit.SECONDS); } } String args = (String)attributes.get("args"); args = args == null ? "" : args + " "; args += jettyHome.resolve("etc/jetty-halt.xml"); try (JettyHomeTester.Run run = jetty.start(args.split(" "))) { run.awaitFor(15, TimeUnit.SECONDS); String output = captureOutput(document, attributes, run); reader.pushInclude(output, "jettyHome_run", target, 1, attributes); } } catch (Throwable x) { reader.pushInclude(x.toString(), "jettyHome_run", target, 1, attributes); x.printStackTrace(); } } private String captureOutput(Document document, Map attributes, JettyHomeTester.Run run) { Stream lines = run.getLogs().stream() .map(line -> redact(line, System.getProperty("java.home"), "/path/to/java.home")) .map(line -> redact(line, run.getConfig().getMavenLocalRepository(), "/path/to/maven.repository")) .map(line -> redact(line, run.getConfig().getJettyHome().toString(), "/path/to/jetty.home")) .map(line -> redact(line, run.getConfig().getJettyBase().toString(), "/path/to/jetty.base")) .map(line -> regexpRedact(line, "(^| )[^ ]+/etc/jetty-halt\\.xml", "")) .map(line -> redact(line, (String)document.getAttribute("project-version"), (String)document.getAttribute("version"))); lines = replace(lines, (String)attributes.get("replace")); lines = delete(lines, (String)attributes.get("delete")); lines = denoteLineStart(lines); lines = highlight(lines, (String)attributes.get("highlight")); lines = callouts(lines, (String)attributes.get("callouts")); return lines.collect(Collectors.joining(System.lineSeparator())); } private String redact(String line, String target, String replacement) { if (target != null && replacement != null) return line.replace(target, replacement); return line; } private String regexpRedact(String line, String regexp, String replacement) { if (regexp != null && replacement != null) return line.replaceAll(regexp, replacement); return line; } private Stream replace(Stream lines, String replace) { if (replace == null) return lines; // Format is: (regexp,replacement). String[] parts = replace.split(","); String regExp = parts[0]; String replacement = parts[1].replace("\\n", "\n"); return lines.flatMap(line -> Stream.of(line.replaceAll(regExp, replacement).split("\n"))); } private Stream delete(Stream lines, String delete) { if (delete == null) return lines; Pattern regExp = Pattern.compile(delete); return lines.filter(line -> !regExp.matcher(line).find()); } private Stream denoteLineStart(Stream lines) { // Matches lines that start with a date such as "2020-01-01 00:00:00.000:". Pattern regExp = Pattern.compile("(^\\d{4}[^:]+:[^:]+:[^:]+:)"); return lines.map(line -> { Matcher matcher = regExp.matcher(line); if (!matcher.find()) return line; return "**" + matcher.group(1) + "**" + line.substring(matcher.end(1)); }); } private Stream highlight(Stream lines, String highlight) { if (highlight == null) return lines; Pattern regExp = Pattern.compile(highlight); return lines.map(line -> { Matcher matcher = regExp.matcher(line); if (!matcher.find()) return line; int groupCount = matcher.groupCount(); // No capturing groups, highlight the whole line. if (groupCount == 0) return "##" + line + "##"; // Highlight the capturing groups. StringBuilder result = new StringBuilder(line.length() + 4 * groupCount); int start = 0; for (int groupIndex = 1; groupIndex <= groupCount; ++groupIndex) { int matchBegin = matcher.start(groupIndex); result.append(line, start, matchBegin); result.append("##"); int matchEnd = matcher.end(groupIndex); result.append(line, matchBegin, matchEnd); result.append("##"); start = matchEnd; } result.append(line, start, line.length()); return result.toString(); }); } private Stream callouts(Stream lines, String callouts) { if (callouts == null) return lines; // Format is (prefix$Nsuffix,regExp...). String[] parts = callouts.split(","); String calloutPattern = parts[0]; List regExps = Stream.of(parts) .skip(1) .map(Pattern::compile) .collect(Collectors.toList()); AtomicInteger index = new AtomicInteger(); return lines.map(line -> { int regExpIndex = index.get(); if (regExpIndex == regExps.size()) return line; Pattern regExp = regExps.get(regExpIndex); if (!regExp.matcher(line).find()) return line; int calloutIndex = index.incrementAndGet(); return line + calloutPattern.replace("$N", String.valueOf(calloutIndex)); }); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy