diff --git a/docs/guides/pom.xml b/docs/guides/pom.xml
index ee41550a9b..a8b6d203ed 100644
--- a/docs/guides/pom.xml
+++ b/docs/guides/pom.xml
@@ -37,6 +37,12 @@
keycloak-guides-maven-plugin
${project.version}
+
+ org.apache.maven
+ maven-plugin-api
+ ${maven.version}
+ test
+
diff --git a/docs/maven-plugin/pom.xml b/docs/maven-plugin/pom.xml
index 67925550f0..e2953f6f89 100644
--- a/docs/maven-plugin/pom.xml
+++ b/docs/maven-plugin/pom.xml
@@ -36,6 +36,7 @@
org.apache.maven
maven-plugin-api
${maven.version}
+ provided
org.apache.maven.plugin-tools
@@ -47,6 +48,7 @@
org.apache.maven
maven-core
${maven.version}
+ provided
org.keycloak
@@ -56,6 +58,25 @@
org.freemarker
freemarker
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ org.jboss.logmanager.LogManager
+
+
+
+
+
+
diff --git a/docs/maven-plugin/src/main/java/org/keycloak/guides/maven/DirectoryCopyVisitor.java b/docs/maven-plugin/src/main/java/org/keycloak/guides/maven/DirectoryCopyVisitor.java
new file mode 100644
index 0000000000..4811c5915f
--- /dev/null
+++ b/docs/maven-plugin/src/main/java/org/keycloak/guides/maven/DirectoryCopyVisitor.java
@@ -0,0 +1,39 @@
+package org.keycloak.guides.maven;
+
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.BasicFileAttributes;
+
+public class DirectoryCopyVisitor extends SimpleFileVisitor {
+
+ private Path targetDir;
+ private Path sourceDir;
+
+ public DirectoryCopyVisitor(Path targetDir) {
+ this.targetDir = targetDir;
+ }
+
+ @Override
+ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
+ if (sourceDir == null) {
+ sourceDir = dir;
+ } else {
+ Path relativePath = sourceDir.relativize(dir);
+ Files.createDirectories(targetDir.resolve(relativePath));
+ }
+
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ Path relativePath = sourceDir.relativize(file);
+ Files.copy(file, targetDir.resolve(relativePath), StandardCopyOption.REPLACE_EXISTING);
+ return FileVisitResult.CONTINUE;
+ }
+
+}
diff --git a/docs/maven-plugin/src/main/java/org/keycloak/guides/maven/GuideMojo.java b/docs/maven-plugin/src/main/java/org/keycloak/guides/maven/GuideMojo.java
index 203b52f745..14172fdb37 100644
--- a/docs/maven-plugin/src/main/java/org/keycloak/guides/maven/GuideMojo.java
+++ b/docs/maven-plugin/src/main/java/org/keycloak/guides/maven/GuideMojo.java
@@ -7,9 +7,9 @@ import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
-import org.codehaus.plexus.util.FileUtils;
import java.io.File;
+import java.nio.file.Files;
@Mojo(name = "keycloak-guide", defaultPhase = LifecyclePhase.GENERATE_SOURCES, threadSafe = true)
public class GuideMojo extends AbstractMojo {
@@ -29,7 +29,7 @@ public class GuideMojo extends AbstractMojo {
Log log = getLog();
File topDir = new File(sourceDir);
- for (File srcDir: topDir.listFiles(d -> d.isDirectory() && !d.getName().equals("templates"))) {
+ for (File srcDir : topDir.listFiles(d -> d.isDirectory() && !d.getName().equals("templates"))) {
if (srcDir.getName().equals("target") || srcDir.getName().equals("src")) {
// those are standard maven folders, ignore them
continue;
@@ -41,7 +41,8 @@ public class GuideMojo extends AbstractMojo {
}
if (srcDir.getName().equals("images")) {
- FileUtils.copyDirectoryStructure(srcDir, targetDir);
+ log.info("Copy files from " + srcDir + " to " + targetDir);
+ Files.walkFileTree(srcDir.toPath(), new DirectoryCopyVisitor(targetDir.toPath()));
} else {
log.info("Guide dir: " + srcDir.getAbsolutePath());
log.info("Target dir: " + targetDir.getAbsolutePath());
diff --git a/docs/maven-plugin/src/test/java/org/keycloak/guides/maven/DirectoryCopyVisitorTest.java b/docs/maven-plugin/src/test/java/org/keycloak/guides/maven/DirectoryCopyVisitorTest.java
new file mode 100644
index 0000000000..e3025d40c5
--- /dev/null
+++ b/docs/maven-plugin/src/test/java/org/keycloak/guides/maven/DirectoryCopyVisitorTest.java
@@ -0,0 +1,55 @@
+package org.keycloak.guides.maven;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+class DirectoryCopyVisitorTest {
+
+ @TempDir
+ Path temp;
+
+ private Path srcDir;
+ private Path targetDir;
+
+ @BeforeEach
+ void setUpDirectories() throws IOException {
+ srcDir = temp.resolve("source");
+ targetDir = temp.resolve("target");
+ Files.createDirectories(srcDir);
+ Files.createDirectories(targetDir);
+ }
+
+ @Test
+ void copyDirectoriesMultipleLevels() throws IOException {
+ Path level1 = srcDir.resolve("level1");
+ Path level2a = level1.resolve("level2a");
+ Path level2b = level1.resolve("level2b");
+ Path level3 = level2a.resolve("level3");
+ Files.createDirectories(level3);
+ Files.createDirectories(level2b);
+ Files.createFile(srcDir.resolve("rootfile"));
+ Files.createFile(level1.resolve("l1file"));
+ Files.createFile(level2b.resolve("l2filea"));
+ Files.createFile(level2b.resolve("l2fileb"));
+
+ Files.walkFileTree(srcDir, new DirectoryCopyVisitor(targetDir));
+
+ assertEquals(List.of("level1", "rootfile"), listDirContent(targetDir));
+ assertEquals(List.of("l1file", "level2a", "level2b"), listDirContent(targetDir.resolve("level1")));
+ assertEquals(List.of("level3"), listDirContent(targetDir.resolve("level1").resolve("level2a")));
+ assertEquals(List.of(), listDirContent(targetDir.resolve("level1").resolve("level2a").resolve("level3")));
+ assertEquals(List.of("l2filea", "l2fileb"), listDirContent(targetDir.resolve("level1").resolve("level2b")));
+ }
+
+ private List listDirContent(Path path) throws IOException {
+ return Files.list(path).map(Path::getFileName).map(Path::toString).sorted().toList();
+ }
+}