Generated ")
+ .append(escapeHtml(Instant.now().toString()))
+ .append(" from ")
+ .append(escapeHtml(modules.getSource().toString()))
+ .append(" using diagram style ")
+ .append(escapeHtml(diagramStyle.name()))
+ .append(".
\n");
+
+ for (GeneratedDiagram diagram : diagrams) {
+ html.append(" \n")
+ .append(" ")
+ .append(escapeHtml(diagram.title()))
+ .append("
\n")
+ .append(" ")
+ .append(escapeHtml(diagram.description()))
+ .append("
\n")
+ .append(" PlantUML source");
+
+ if (diagram.svgLocation() != null) {
+ html.append("SVG");
+ }
+
+ html.append("
\n");
+
+ if (diagram.svgLocation() != null) {
+ html.append(" )
\n");
+ } else {
+ html.append(" No SVG preview is available for this diagram.");
+
+ if (diagram.failureMessage() != null && !diagram.failureMessage().isBlank()) {
+ html.append(" PlantUML rendering reported: ")
+ .append(escapeHtml(diagram.failureMessage()))
+ .append('.');
+ }
+
+ html.append(" The exact PlantUML source is included below.
\n");
+ }
+
+ html.append(" Show PlantUML source
")
+ .append(escapeHtml(diagram.source()))
+ .append(" \n")
+ .append(" \n");
+ }
+
+ html.append("\n\n");
+
+ return html.toString();
+ }
+
+ private static String escapeHtml(String value) {
+ return value.replace("&", "&")
+ .replace("<", "<")
+ .replace(">", ">")
+ .replace("\"", """)
+ .replace("'", "'");
+ }
+
+ private static void deleteRecursively(Path directory) {
+
+ if (directory == null) {
+ return;
+ }
+
+ try {
+ if (!Files.exists(directory)) {
+ return;
+ }
+
+ Files.walk(directory)
+ .sorted((left, right) -> right.compareTo(left))
+ .forEach(path -> {
+ try {
+ Files.deleteIfExists(path);
+ } catch (IOException ex) {
+ throw new UncheckedIOException(ex);
+ }
+ });
+ } catch (IOException ex) {
+ throw new UncheckedIOException(ex);
+ }
+ }
+
+ private record GeneratedDiagram(String title, String description, String pumlLocation, String svgLocation,
+ String source, String failureMessage) {}
+}
+
diff --git a/spring-modulith-maven-plugin/src/main/java/org/springframework/modulith/maven/PlantUmlSvgRenderer.java b/spring-modulith-maven-plugin/src/main/java/org/springframework/modulith/maven/PlantUmlSvgRenderer.java
new file mode 100644
index 00000000..caa70b3e
--- /dev/null
+++ b/spring-modulith-maven-plugin/src/main/java/org/springframework/modulith/maven/PlantUmlSvgRenderer.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2026 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.modulith.maven;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
+
+import net.sourceforge.plantuml.FileFormat;
+import net.sourceforge.plantuml.FileFormatOption;
+import net.sourceforge.plantuml.SourceStringReader;
+
+final class PlantUmlSvgRenderer implements DiagramRenderer {
+
+ private static final String START = "@startuml";
+ private static final String LAYOUT = "!pragma layout smetana";
+
+ @Override
+ public RenderedDiagram render(String source) {
+
+ try {
+
+ var output = new ByteArrayOutputStream();
+ var reader = new SourceStringReader(prepare(source));
+ var description = reader.outputImage(output, new FileFormatOption(FileFormat.SVG));
+ var svg = output.toString(StandardCharsets.UTF_8);
+
+ if (svg.isBlank()) {
+ return new RenderedDiagram(null, description == null ? "PlantUML did not produce SVG output."
+ : description.getDescription());
+ }
+
+ return new RenderedDiagram(svg, null);
+
+ } catch (Exception ex) {
+ return new RenderedDiagram(null, ex.getMessage());
+ }
+ }
+
+ private static String prepare(String source) {
+
+ if (!source.contains(START) || source.contains(LAYOUT)) {
+ return source;
+ }
+
+ return source.replaceFirst(START, START + System.lineSeparator() + System.lineSeparator() + LAYOUT);
+ }
+}
+
+
diff --git a/spring-modulith-maven-plugin/src/main/java/org/springframework/modulith/maven/ProjectClassLoaderFactory.java b/spring-modulith-maven-plugin/src/main/java/org/springframework/modulith/maven/ProjectClassLoaderFactory.java
new file mode 100644
index 00000000..5b2f1f13
--- /dev/null
+++ b/spring-modulith-maven-plugin/src/main/java/org/springframework/modulith/maven/ProjectClassLoaderFactory.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2026 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.modulith.maven;
+
+import java.io.File;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+
+import org.apache.maven.artifact.DependencyResolutionRequiredException;
+import org.apache.maven.project.MavenProject;
+
+final class ProjectClassLoaderFactory {
+
+ URLClassLoader create(MavenProject project) throws DependencyResolutionRequiredException {
+
+ var elements = new LinkedHashSet