Skip to content

Commit

Permalink
Use Maven Eclipse Sisu to inject components (asciidoctor#977)
Browse files Browse the repository at this point in the history
* Refactor asciidoctor-maven-plugin to use DI
* Refactor Doxia modules to use DI
* Refactors in Doxia modules to reduce code duplication
* Refactor SiteConversionConfigurationParser to reduce duplication and
return only '<asciidoc>' configuration always
* Centralize Maven4 configurations in a profile in the parent. Required to
modify dependencies and properties.

Notes:
* Log instance passed to methods for simplicity. With Maven 4 that will
be removed in favor of normal Slf4j.
* This already helped with refactors
* Constructor injection reduced the need for reflection in unit tests

Fixes asciidoctor#945
  • Loading branch information
abelsromero authored Nov 10, 2024
1 parent 4d21189 commit 7669152
Show file tree
Hide file tree
Showing 30 changed files with 897 additions and 621 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-maven-4.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,5 @@ jobs:
- name: Maven version
run: mvn -version
- name: Build & Test
run: mvn -B -Prun-its clean verify -D"maven.version=${{ matrix.maven }}" -D"plexus-utils.version=4.0.2"
run: mvn -B -Prun-its -Pmaven4 clean verify

5 changes: 5 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ For a detailed view of what has changed, refer to the {uri-repo}/commits/main[co

== Unreleased

Improvements (for all modules)::

* Use Maven Eclipse Sisu to inject components (allowed refactors to increase code re-use) (#945)
* Centralize Maven4 configurations in a profile in the parent (#945)

Bug Fixes (Site Modules)::

* Fix resolution of baseDir for Doxia modules (#968)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.asciidoctor.maven.site;

import javax.inject.Inject;
import javax.inject.Provider;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
Expand All @@ -12,14 +11,8 @@
import org.apache.maven.doxia.sink.Sink;
import org.apache.maven.project.MavenProject;
import org.asciidoctor.Asciidoctor;
import org.asciidoctor.Attributes;
import org.asciidoctor.AttributesBuilder;
import org.asciidoctor.Options;
import org.asciidoctor.OptionsBuilder;
import org.asciidoctor.SafeMode;
import org.asciidoctor.maven.commons.StringUtils;
import org.asciidoctor.maven.log.LogHandler;
import org.asciidoctor.maven.log.LogRecordFormatter;
import org.asciidoctor.maven.log.LogRecordsProcessors;
import org.asciidoctor.maven.log.MemoryLogHandler;
import org.asciidoctor.maven.site.SiteConverterDecorator.Result;
Expand All @@ -29,8 +22,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static org.asciidoctor.maven.site.SiteBaseDirResolver.resolveBaseDir;

/**
* This class is used by <a href="https://maven.apache.org/doxia/overview.html">the Doxia framework</a>
* to handle the actual parsing of the AsciiDoc input files into HTML to be consumed/wrapped
Expand All @@ -43,15 +34,28 @@
@Component(role = Parser.class, hint = AsciidoctorConverterDoxiaParser.ROLE_HINT)
public class AsciidoctorConverterDoxiaParser extends AbstractTextParser {

private static final Logger logger = LoggerFactory.getLogger(AsciidoctorConverterDoxiaParser.class);

@Inject
protected Provider<MavenProject> mavenProjectProvider;

/**
* The role hint for the {@link AsciidoctorConverterDoxiaParser} Plexus component.
*/
public static final String ROLE_HINT = "asciidoc";
static final String ROLE_HINT = "asciidoc";

private static final Logger logger = LoggerFactory.getLogger(AsciidoctorConverterDoxiaParser.class);

private final MavenProject mavenProject;
private final SiteConversionConfigurationParser siteConfigParser;
private final LogHandlerFactory logHandlerFactory;
private final SiteConverterDecorator siteConverter;

@Inject
public AsciidoctorConverterDoxiaParser(MavenProject mavenProject,
SiteConversionConfigurationParser siteConfigParser,
LogHandlerFactory logHandlerFactory,
SiteConverterDecorator siteConverter) {
this.mavenProject = mavenProject;
this.siteConfigParser = siteConfigParser;
this.logHandlerFactory = logHandlerFactory;
this.siteConverter = siteConverter;
}

/**
* {@inheritDoc}
Expand All @@ -68,27 +72,24 @@ public void parse(Reader reader, Sink sink, String reference) throws ParseExcept
return;
}

final MavenProject project = mavenProjectProvider.get();
final Xpp3Dom siteConfig = getSiteConfig(project);
final File siteDirectory = resolveBaseDir(project.getBasedir(), siteConfig);
final SiteConversionConfiguration conversionConfig = siteConfigParser.processAsciiDocConfig(mavenProject, ROLE_HINT);
final Xpp3Dom asciidocConfig = conversionConfig.getAsciidocConfig();
final File siteDirectory = conversionConfig.getSiteBaseDir();

// Doxia handles a single instance of this class and invokes it multiple times.
// We need to ensure certain elements are initialized only once to avoid errors.
// Note, this cannot be done in the constructor because mavenProjectProvider in set after construction.
// And overriding init and other methods form parent classes does not work.
final Asciidoctor asciidoctor = Asciidoctor.Factory.create();

SiteConversionConfiguration conversionConfig = new SiteConversionConfigurationParser(project)
.processAsciiDocConfig(siteConfig, defaultOptions(siteDirectory), defaultAttributes());
for (String require : conversionConfig.getRequires()) {
requireLibrary(asciidoctor, require);
}

final LogHandler logHandler = getLogHandlerConfig(siteConfig);
final MemoryLogHandler memoryLogHandler = asciidoctorLoggingSetup(asciidoctor, siteDirectory);
final LogHandler logHandler = logHandlerFactory.getConfiguration(asciidocConfig);
final MemoryLogHandler memoryLogHandler = logHandlerFactory.create(asciidoctor, siteDirectory, logger);

final SiteConverterDecorator siteConverter = new SiteConverterDecorator(asciidoctor);
final Result headerMetadata = siteConverter.process(source, conversionConfig.getOptions());
final Result headerMetadata = siteConverter.process(asciidoctor, source, conversionConfig.getOptions());

try {
// process log messages according to mojo configuration
Expand All @@ -97,7 +98,7 @@ public void parse(Reader reader, Sink sink, String reference) throws ParseExcept
if (logHandler.getOutputToConsole() && StringUtils.isNotBlank(reference)) {
memoryLogHandler.processAll();
}
new LogRecordsProcessors(logHandler, siteDirectory, errorMessage -> logger.error(errorMessage))
new LogRecordsProcessors(logHandler, siteDirectory, logger::error)
.processLogRecords(memoryLogHandler);
}
} catch (Exception exception) {
Expand All @@ -110,45 +111,6 @@ public void parse(Reader reader, Sink sink, String reference) throws ParseExcept
sink.rawText(headerMetadata.getHtml());
}

private MemoryLogHandler asciidoctorLoggingSetup(Asciidoctor asciidoctor, File siteDirectory) {

final MemoryLogHandler memoryLogHandler = new MemoryLogHandler(false,
logRecord -> logger.info(LogRecordFormatter.format(logRecord, siteDirectory)));
asciidoctor.registerLogHandler(memoryLogHandler);
// disable default console output of AsciidoctorJ
java.util.logging.Logger.getLogger("asciidoctor").setUseParentHandlers(false);
return memoryLogHandler;
}

private LogHandler getLogHandlerConfig(Xpp3Dom siteConfig) {
Xpp3Dom asciidoc = siteConfig == null ? null : siteConfig.getChild("asciidoc");
return new SiteLogHandlerDeserializer().deserialize(asciidoc);
}

protected Xpp3Dom getSiteConfig(MavenProject project) {
return project.getGoalConfiguration("org.apache.maven.plugins", "maven-site-plugin", "site", "site");
}


// The possible baseDir based on configuration are:
//
// with nothing : src/site + /asciidoc
// with locale : src/site + {locale} + /asciidoc
// with siteDirectory : {siteDirectory} + /asciidoc
// with siteDirectory + locale : {siteDirectory} + {locale} + /asciidoc
protected OptionsBuilder defaultOptions(File siteDirectory) {
return Options.builder()
.backend("xhtml")
.safe(SafeMode.UNSAFE)
.baseDir(new File(siteDirectory, ROLE_HINT).getAbsoluteFile());
}

protected AttributesBuilder defaultAttributes() {
return Attributes.builder()
.attribute("idprefix", "@")
.attribute("showtitle", "@");
}

private void requireLibrary(Asciidoctor asciidoctor, String require) {
if (!(require = require.trim()).isEmpty()) {
try {
Expand All @@ -158,4 +120,5 @@ private void requireLibrary(Asciidoctor asciidoctor, String require) {
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* <a href="https://maven.apache.org/doxia/references/">Doxia provided modules</a>.
*
* @author jdlee
* @since 0.1.2.1
*/
@Component(role = ParserModule.class, hint = AsciidoctorConverterDoxiaParser.ROLE_HINT)
public class AsciidoctorConverterDoxiaParserModule extends AbstractParserModule {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.asciidoctor.maven.site;

import javax.inject.Singleton;
import java.util.Map;

import org.asciidoctor.Asciidoctor;
Expand All @@ -10,16 +11,14 @@
/**
* Asciidoctor conversion wrapper for maven-site integration.
* In addition to conversion, handles header metadata extraction.
*
* @author abelsromero
* @since 3.0.0
*/
@Singleton
class SiteConverterDecorator {

private final Asciidoctor asciidoctor;

SiteConverterDecorator(Asciidoctor asciidoctor) {
this.asciidoctor = asciidoctor;
}

Result process(String content, Options options) {
Result process(Asciidoctor asciidoctor, String content, Options options) {
final Document document = asciidoctor.load(content, headerProcessingMetadata(options));
final HeaderMetadata headerMetadata = HeaderMetadata.from(document);

Expand Down Expand Up @@ -61,5 +60,4 @@ String getHtml() {
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import static org.asciidoctor.maven.site.AsciidoctorConverterDoxiaParser.ROLE_HINT;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable;
import static org.codehaus.plexus.util.ReflectionUtils.setVariableValueInObject;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;

Expand Down Expand Up @@ -247,14 +246,14 @@ void should_fail_when_logHandler_failIf_is_WARNING() {
}

@SneakyThrows
private javax.inject.Provider<MavenProject> createMavenProjectMock(String configuration) {
private MavenProject createMockMavenProject(String configuration) {
MavenProject mockProject = Mockito.mock(MavenProject.class);
when(mockProject.getBasedir())
.thenReturn(new File("."));
when(mockProject.getGoalConfiguration(anyString(), anyString(), anyString(), anyString()))
.thenReturn(configuration != null ? Xpp3DomBuilder.build(new StringReader(configuration)) : null);

return () -> mockProject;
return mockProject;
}

@SneakyThrows
Expand All @@ -264,16 +263,19 @@ private AsciidoctorConverterDoxiaParser mockAsciidoctorDoxiaParser() {

@SneakyThrows
private AsciidoctorConverterDoxiaParser mockAsciidoctorDoxiaParser(String configuration) {
AsciidoctorConverterDoxiaParser parser = new AsciidoctorConverterDoxiaParser();
setVariableValueInObject(parser, "mavenProjectProvider", createMavenProjectMock(configuration));
return parser;
return new AsciidoctorConverterDoxiaParser(
createMockMavenProject(configuration),
new SiteConversionConfigurationParser(new SiteBaseDirResolver()),
new LogHandlerFactory(),
new SiteConverterDecorator()
);
}

private Sink createSinkMock() {
return new TextProviderSink();
}

class TextProviderSink extends AbstractTextSink {
static class TextProviderSink extends AbstractTextSink {
String text;

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ class SiteConverterDecoratorTest {
"Hello, AsciiDoc!\n================"
})
void should_extract_title_from_header(String title) {
SiteConverterDecorator siteConverter = new SiteConverterDecorator(asciidoctor);
SiteConverterDecorator siteConverter = new SiteConverterDecorator();

Options options = defaultOptions();
Result result = siteConverter.process(title + "\n", options);
Result result = siteConverter.process(asciidoctor, title + "\n", options);

HeaderMetadata headerMetadata = result.getHeaderMetadata();
assertThat(headerMetadata.getTitle()).isEqualTo("Hello, AsciiDoc!");
Expand All @@ -40,7 +40,7 @@ void should_extract_title_from_header(String title) {

@Test
void should_extract_title_from_attribute() {
SiteConverterDecorator siteConverter = new SiteConverterDecorator(asciidoctor);
SiteConverterDecorator siteConverter = new SiteConverterDecorator();

Options options = Options.builder()
.safe(SafeMode.UNSAFE)
Expand All @@ -51,7 +51,7 @@ void should_extract_title_from_attribute() {
.attribute("who", "me")
.build())
.build();
Result result = siteConverter.process("= Hello, {who}!\n", options);
Result result = siteConverter.process(asciidoctor, "= Hello, {who}!\n", options);

HeaderMetadata headerMetadata = result.getHeaderMetadata();
assertThat(headerMetadata.getTitle()).isEqualTo("Hello, me!");
Expand All @@ -62,10 +62,10 @@ void should_extract_title_from_attribute() {
@ParameterizedTest
@MethodSource("authorsProvider")
void should_extract_author(String content, String expected) {
SiteConverterDecorator siteConverter = new SiteConverterDecorator(asciidoctor);
SiteConverterDecorator siteConverter = new SiteConverterDecorator();

Options options = defaultOptions();
Result result = siteConverter.process(content + "\n", options);
Result result = siteConverter.process(asciidoctor, content + "\n", options);

HeaderMetadata headerMetadata = result.getHeaderMetadata();
assertThat(headerMetadata.getAuthors())
Expand All @@ -84,11 +84,11 @@ private static Stream<Arguments> authorsProvider() {

@Test
void should_extract_author_from_attribute() {
SiteConverterDecorator siteConverter = new SiteConverterDecorator(asciidoctor);
SiteConverterDecorator siteConverter = new SiteConverterDecorator();

String content = "= Hello, AsciiDoc!";
Options options = optionsWithAttributes(Collections.singletonMap("author", "From Attr"));
Result result = siteConverter.process(content + "\n", options);
Result result = siteConverter.process(asciidoctor, content + "\n", options);

HeaderMetadata headerMetadata = result.getHeaderMetadata();
assertThat(headerMetadata.getAuthors())
Expand All @@ -98,10 +98,10 @@ void should_extract_author_from_attribute() {

@Test
void should_extract_multiple_authors() {
SiteConverterDecorator siteConverter = new SiteConverterDecorator(asciidoctor);
SiteConverterDecorator siteConverter = new SiteConverterDecorator();

String content = "= Hello, AsciiDoc!\nfirstname1 lastname2; firstname3 middlename4 lastname5";
Result result = siteConverter.process(content + "\n", defaultOptions());
Result result = siteConverter.process(asciidoctor, content + "\n", defaultOptions());

HeaderMetadata headerMetadata = result.getHeaderMetadata();
assertThat(headerMetadata.getAuthors())
Expand All @@ -111,10 +111,10 @@ void should_extract_multiple_authors() {

@Test
void should_extract_datetime_generated() {
SiteConverterDecorator siteConverter = new SiteConverterDecorator(asciidoctor);
SiteConverterDecorator siteConverter = new SiteConverterDecorator();

String content = "= Hello, AsciiDoc!";
Result result = siteConverter.process(content + "\n", defaultOptions());
Result result = siteConverter.process(asciidoctor, content + "\n", defaultOptions());

HeaderMetadata headerMetadata = result.getHeaderMetadata();
assertThat(headerMetadata.getDateTime()).matches("(\\d{4})-(\\d{2})-(\\d{2}) (\\d{2}):(\\d{2}):(\\d{2}) .*");
Expand All @@ -123,11 +123,11 @@ void should_extract_datetime_generated() {

@Test
void should_extract_datetime_from_attribute() {
SiteConverterDecorator siteConverter = new SiteConverterDecorator(asciidoctor);
SiteConverterDecorator siteConverter = new SiteConverterDecorator();

String content = "= Hello, AsciiDoc!";
Options options = optionsWithAttributes(Collections.singletonMap("docdatetime", "2024-11-22"));
Result result = siteConverter.process(content + "\n", options);
Result result = siteConverter.process(asciidoctor, content + "\n", options);

HeaderMetadata headerMetadata = result.getHeaderMetadata();
assertThat(headerMetadata.getDateTime()).isEqualTo("2024-11-22");
Expand Down
5 changes: 1 addition & 4 deletions asciidoctor-maven-commons/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@
</description>
<url>https://github.com/asciidoctor/asciidoctor-maven-plugin</url>

<properties>
<plexus-utils.version>3.5.1</plexus-utils.version>
</properties>

<dependencies>
<dependency>
<groupId>org.asciidoctor</groupId>
Expand All @@ -37,6 +33,7 @@
<version>${maven.version}</version>
<scope>provided</scope>
</dependency>
<!-- Required: see https://issues.apache.org/jira/browse/MNG-6965 -->
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-utils</artifactId>
Expand Down
Loading

0 comments on commit 7669152

Please sign in to comment.