diff --git a/flow-tests/pom.xml b/flow-tests/pom.xml index a316d648d86..873393c636d 100644 --- a/flow-tests/pom.xml +++ b/flow-tests/pom.xml @@ -46,7 +46,6 @@ false 0 true - @@ -229,7 +228,6 @@ ${vaadin.devmode.vite.options} ${vaadin.frontend.hotdeploy} - ${vaadin.frontend.hotdeploy.dependencies} diff --git a/flow-tests/test-live-reload-multimodule/ui/pom.xml b/flow-tests/test-live-reload-multimodule/ui/pom.xml index cef972c46ec..a241bd03286 100644 --- a/flow-tests/test-live-reload-multimodule/ui/pom.xml +++ b/flow-tests/test-live-reload-multimodule/ui/pom.xml @@ -14,7 +14,6 @@ war true - ../library diff --git a/vaadin-dev-server/src/main/java/com/vaadin/base/devserver/ExternalDependencyWatcher.java b/vaadin-dev-server/src/main/java/com/vaadin/base/devserver/ExternalDependencyWatcher.java index a9ea7bad05c..a197ca5f369 100644 --- a/vaadin-dev-server/src/main/java/com/vaadin/base/devserver/ExternalDependencyWatcher.java +++ b/vaadin-dev-server/src/main/java/com/vaadin/base/devserver/ExternalDependencyWatcher.java @@ -30,6 +30,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; import com.vaadin.flow.server.InitParameters; import com.vaadin.flow.server.VaadinContext; @@ -44,25 +45,48 @@ public ExternalDependencyWatcher(VaadinContext context, File jarFrontendResourcesFolder) { ApplicationConfiguration config = ApplicationConfiguration.get(context); - String hotdeployDependencies = config.getStringProperty( + String hotdeployDependenciesProperty = config.getStringProperty( InitParameters.FRONTEND_HOTDEPLOY_DEPENDENCIES, null); List hotdeployDependencyFolders = new ArrayList<>(); - if (hotdeployDependencies != null) { - for (String folder : hotdeployDependencies.split(",")) { + File projectFolder = config.getProjectFolder(); + if (hotdeployDependenciesProperty != null) { + for (String folder : hotdeployDependenciesProperty.split(",")) { if (!folder.isBlank()) { hotdeployDependencyFolders.add(folder.trim()); } } + } else { + File pomFile = new File(projectFolder, "pom.xml"); + File parentPomFile = MavenUtils + .getParentPomOfMultiModuleProject(pomFile); + if (parentPomFile != null) { + Document parentPom = MavenUtils.parsePomFile(parentPomFile); + if (parentPom != null) { + Path currentPomToParentPomPath = pomFile.getParentFile() + .toPath() + .relativize(parentPomFile.getParentFile().toPath()); + hotdeployDependencyFolders = MavenUtils + .getModuleFolders(parentPom).stream() + .map(folder -> currentPomToParentPomPath + + File.separator + folder) + .toList(); + } + } } for (String hotdeployDependencyFolder : hotdeployDependencyFolders) { - Path moduleFolder = config.getProjectFolder().toPath() - .resolve(hotdeployDependencyFolder); + Path moduleFolder = projectFolder.toPath() + .resolve(hotdeployDependencyFolder).normalize(); + if (moduleFolder.equals(projectFolder.toPath())) { + // Don't watch the active module + continue; + } Path metaInf = moduleFolder .resolve(Path.of("src", "main", "resources", "META-INF")); if (!watchDependencyFolder(metaInf.toFile(), - jarFrontendResourcesFolder)) { + jarFrontendResourcesFolder) + && hotdeployDependenciesProperty != null) { getLogger().warn("No folders to watch were found in " + metaInf.normalize().toAbsolutePath() + ". This should be the META-INF folder that contains either frontend or resources/frontend"); diff --git a/vaadin-dev-server/src/main/java/com/vaadin/base/devserver/MavenUtils.java b/vaadin-dev-server/src/main/java/com/vaadin/base/devserver/MavenUtils.java index 827cb3cc76e..142c972b3a0 100644 --- a/vaadin-dev-server/src/main/java/com/vaadin/base/devserver/MavenUtils.java +++ b/vaadin-dev-server/src/main/java/com/vaadin/base/devserver/MavenUtils.java @@ -4,6 +4,9 @@ import javax.xml.parsers.DocumentBuilderFactory; import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.stream.Stream; import java.util.stream.Stream.Builder; @@ -11,7 +14,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; -import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -80,7 +82,7 @@ public static Document parsePomFile(File pomFile) { * @return Text content of the first mach or null if not found. */ static String getFirstElementTextByName(Node parent, String nodeName) { - return findChild(parent, nodeName).map(node -> node.getTextContent()) + return findChild(parent, nodeName).map(Node::getTextContent) .orElse(null); } @@ -95,7 +97,7 @@ public static String getGroupId(Document pom) { String groupId = getFirstElementTextByName(pom.getDocumentElement(), "groupId"); if (groupId == null) { - groupId = findChild(pom.getDocumentElement(), "parent") + groupId = findParentTag(pom) .map(parentNode -> getFirstElementTextByName(parentNode, "groupId")) .orElse(null); @@ -103,6 +105,10 @@ public static String getGroupId(Document pom) { return groupId; } + private static Optional findParentTag(Document pom) { + return findChild(pom.getDocumentElement(), "parent"); + } + /** * Finds the artifact id for the given pom file. * @@ -115,6 +121,18 @@ public static String getArtifactId(Document pom) { "artifactId"); } + private static Optional getParentArtifactId(Document pom) { + return findParentTag(pom) + .flatMap(parentNode -> findChild(parentNode, "artifactId")) + .map(Node::getTextContent); + } + + private static Optional getParentRelativePath(Document pom) { + return findParentTag(pom) + .flatMap(parentNode -> findChild(parentNode, "relativePath")) + .map(Node::getTextContent); + } + private static Optional findChild(Node node, String tagname) { return findChildren(node, tagname).findFirst(); } @@ -132,4 +150,88 @@ private static Stream findChildren(Node node, String tagname) { return builder.build(); } + /** + * Gets the parent pom location for the given pom file, if the given pom + * file is part of a multi module project. + * + * @param pomFile + * the pom file + * @return the location of the parent pom file or {@code null} if the given + * pom file does not have a parent inside the same multi module + * project + */ + public static File getParentPomOfMultiModuleProject(File pomFile) { + Document pom = parsePomFile(pomFile); + if (pom == null) { + return null; + } + Optional parent = getParentArtifactId(pom); + if (!parent.isPresent()) { + return null; + } + + File pomFolder = pomFile.getParentFile(); + File parentPomFile = getParentRelativePath(pom) + .map(relativePath -> new File(pomFolder, relativePath)) + .map(relativePath -> { + if (!relativePath.isFile()) { + // relative path can refer to a folder + relativePath = new File(relativePath, "pom.xml"); + } + return relativePath; + }).orElse(new File(pomFolder.getParentFile(), "pom.xml")); + + Document parentFolderPom = parsePomFile(parentPomFile); + if (parentFolderPom == null) { + return null; + } + String parentFolderArtifactId = getArtifactId(parentFolderPom); + + if (Objects.equals(parent.get(), parentFolderArtifactId)) { + try { + return parentPomFile.getCanonicalFile(); + } catch (IOException e) { + return parentPomFile; + } + } + return null; + + } + + /** + * Gets a list of the folders containing the sub modules for the given pom + * file. + * + * @param pom + * the pom file containing sub modules + * @return a list of folders for the sub modules + */ + public static List getModuleFolders(Document pom) { + return findChild(pom.getDocumentElement(), "modules").stream() + .flatMap(node -> findChildren(node, "module")) + .map(Node::getTextContent) + .map(possiblyFilename -> removeAfter(possiblyFilename, "/")) + .toList(); + } + + /** + * Removes the part of the given string that comes after the (last) instance + * of the given delimiter. + * + * Returns the original string if it does not contain the delimiter. + * + * @param str + * the string to parse + * @param delimiter + * the delimiter to look for + * @return the modified string + */ + private static String removeAfter(String str, String delimiter) { + int i = str.lastIndexOf(delimiter); + if (i != -1) { + return str.substring(0, i); + } + return str; + } + } diff --git a/vaadin-dev-server/src/test/java/com/vaadin/base/devserver/MavenUtilsTest.java b/vaadin-dev-server/src/test/java/com/vaadin/base/devserver/MavenUtilsTest.java index 98fee80aa54..6459342f3ea 100644 --- a/vaadin-dev-server/src/test/java/com/vaadin/base/devserver/MavenUtilsTest.java +++ b/vaadin-dev-server/src/test/java/com/vaadin/base/devserver/MavenUtilsTest.java @@ -2,11 +2,12 @@ import java.io.File; import java.io.IOException; -import java.nio.charset.StandardCharsets; +import java.net.URL; +import java.nio.file.Path; +import java.util.List; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; import org.junit.Assert; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -17,8 +18,16 @@ public class MavenUtilsTest { @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); + private File mavenFolder; + + @Before + public void setupPoms() throws Exception { + URL mavenTestResourceDirectory = getClass().getResource("maven"); + this.mavenFolder = Path.of(mavenTestResourceDirectory.toURI()).toFile(); + } + @Test - public void basicInformationForStandalonePom() throws IOException { + public void basicInformationForStandalonePom() throws Exception { File pomXml = getPomXml("pom-standalone.xml"); Document parse = MavenUtils.parsePomFile(pomXml); Assert.assertEquals("this.group", MavenUtils.getGroupId(parse)); @@ -27,21 +36,64 @@ public void basicInformationForStandalonePom() throws IOException { } @Test - public void basicInformationForPomWithParent() throws IOException { - File pomXml = getPomXml("pom-with-parent.xml"); + public void basicInformationForPomWithParent() throws Exception { + File pomXml = getPomXml("standard-multimodule/module1/pom.xml"); Document parse = MavenUtils.parsePomFile(pomXml); Assert.assertEquals("this.group", MavenUtils.getGroupId(parse)); Assert.assertEquals("this-the-artifact", MavenUtils.getArtifactId(parse)); } + @Test + public void detectsPomIsPartOfASimpleMultimoduleProject() throws Exception { + File parent = getPomXml("standard-multimodule/pom.xml"); + Assert.assertEquals(parent, MavenUtils.getParentPomOfMultiModuleProject( + getPomXml("standard-multimodule/module1/pom.xml"))); + } + + @Test + public void detectsPomWithRelativePathIsPartOfASimpleMultimoduleProject() + throws Exception { + File parent = getPomXml("standard-multimodule/pom.xml"); + Assert.assertEquals(parent, MavenUtils.getParentPomOfMultiModuleProject( + getPomXml("standard-multimodule/module2/pom.xml"))); + } + + @Test + public void detectsPomIsPartOfAComplexMultimoduleProject() + throws Exception { + File parent = getPomXml("complex-multimodule/pom-parent.xml"); + File parentPomOfMultiModuleProject = MavenUtils + .getParentPomOfMultiModuleProject(getPomXml( + "complex-multimodule/module1/pom-with-parent.xml")); + Assert.assertEquals(parent, parentPomOfMultiModuleProject); + } + + @Test + public void findsModulesInSimpleMultiModulePom() throws Exception { + File pomXml = getPomXml("standard-multimodule/pom.xml"); + Document pom = MavenUtils.parsePomFile(pomXml); + Assert.assertEquals(List.of("module1", "module2"), + MavenUtils.getModuleFolders(pom)); + } + + @Test + public void findsModulesInComplexMultiModulePom() throws Exception { + File pomXml = getPomXml("complex-multimodule/pom-parent.xml"); + Document pom = MavenUtils.parsePomFile(pomXml); + Assert.assertEquals(List.of("module1", "module2"), + MavenUtils.getModuleFolders(pom)); + } + + @Test + public void findsNoModulesInStandalonePom() throws Exception { + File pomXml = getPomXml("pom-standalone.xml"); + Document pom = MavenUtils.parsePomFile(pomXml); + Assert.assertEquals(List.of(), MavenUtils.getModuleFolders(pom)); + } + private File getPomXml(String filename) throws IOException { - File pomXml = temporaryFolder.newFile(filename); - FileUtils.write(pomXml, - IOUtils.toString(getClass().getResource("maven/" + filename), - StandardCharsets.UTF_8), - StandardCharsets.UTF_8); - return pomXml; + return new File(mavenFolder, filename); } } diff --git a/vaadin-dev-server/src/test/resources/com/vaadin/base/devserver/maven/pom-with-parent.xml b/vaadin-dev-server/src/test/resources/com/vaadin/base/devserver/maven/complex-multimodule/module1/pom-with-parent.xml similarity index 83% rename from vaadin-dev-server/src/test/resources/com/vaadin/base/devserver/maven/pom-with-parent.xml rename to vaadin-dev-server/src/test/resources/com/vaadin/base/devserver/maven/complex-multimodule/module1/pom-with-parent.xml index 61e69e7e51a..6c34e3152c4 100644 --- a/vaadin-dev-server/src/test/resources/com/vaadin/base/devserver/maven/pom-with-parent.xml +++ b/vaadin-dev-server/src/test/resources/com/vaadin/base/devserver/maven/complex-multimodule/module1/pom-with-parent.xml @@ -4,8 +4,10 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 + parent-artifact this.group 1.7.2-SNAPSHOT + ../pom-parent.xml this-the-artifact Something something diff --git a/vaadin-dev-server/src/test/resources/com/vaadin/base/devserver/maven/complex-multimodule/module2/pom.xml b/vaadin-dev-server/src/test/resources/com/vaadin/base/devserver/maven/complex-multimodule/module2/pom.xml new file mode 100644 index 00000000000..f8ed761e505 --- /dev/null +++ b/vaadin-dev-server/src/test/resources/com/vaadin/base/devserver/maven/complex-multimodule/module2/pom.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + + parent-artifact + this.group + 1.7.2-SNAPSHOT + ../pom-parent.xml + + another-artifact + Something something + + + + + diff --git a/vaadin-dev-server/src/test/resources/com/vaadin/base/devserver/maven/complex-multimodule/pom-parent.xml b/vaadin-dev-server/src/test/resources/com/vaadin/base/devserver/maven/complex-multimodule/pom-parent.xml new file mode 100644 index 00000000000..614a5f9fa50 --- /dev/null +++ b/vaadin-dev-server/src/test/resources/com/vaadin/base/devserver/maven/complex-multimodule/pom-parent.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + this.group + parent-artifact + 1.7.2-SNAPSHOT + Something something parent + pom + + module1/pom-with-parent.xml + module2 + + + diff --git a/vaadin-dev-server/src/test/resources/com/vaadin/base/devserver/maven/pom-standalone.xml b/vaadin-dev-server/src/test/resources/com/vaadin/base/devserver/maven/pom-standalone.xml index 947ec0918fc..ff7eb03bda1 100644 --- a/vaadin-dev-server/src/test/resources/com/vaadin/base/devserver/maven/pom-standalone.xml +++ b/vaadin-dev-server/src/test/resources/com/vaadin/base/devserver/maven/pom-standalone.xml @@ -8,7 +8,4 @@ 1.7.2-SNAPSHOT Something something - - - diff --git a/vaadin-dev-server/src/test/resources/com/vaadin/base/devserver/maven/standard-multimodule/module1/pom.xml b/vaadin-dev-server/src/test/resources/com/vaadin/base/devserver/maven/standard-multimodule/module1/pom.xml new file mode 100644 index 00000000000..6b974ec69a5 --- /dev/null +++ b/vaadin-dev-server/src/test/resources/com/vaadin/base/devserver/maven/standard-multimodule/module1/pom.xml @@ -0,0 +1,17 @@ + + + 4.0.0 + + parent-artifact + this.group + 1.7.2-SNAPSHOT + + this-the-artifact + Something something + + + + + diff --git a/vaadin-dev-server/src/test/resources/com/vaadin/base/devserver/maven/standard-multimodule/module2/pom.xml b/vaadin-dev-server/src/test/resources/com/vaadin/base/devserver/maven/standard-multimodule/module2/pom.xml new file mode 100644 index 00000000000..4f4396f620b --- /dev/null +++ b/vaadin-dev-server/src/test/resources/com/vaadin/base/devserver/maven/standard-multimodule/module2/pom.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + + parent-artifact + this.group + 1.7.2-SNAPSHOT + .. + + another-artifact + Something something + + + + + diff --git a/vaadin-dev-server/src/test/resources/com/vaadin/base/devserver/maven/standard-multimodule/pom.xml b/vaadin-dev-server/src/test/resources/com/vaadin/base/devserver/maven/standard-multimodule/pom.xml new file mode 100644 index 00000000000..a6cd1b1dc8b --- /dev/null +++ b/vaadin-dev-server/src/test/resources/com/vaadin/base/devserver/maven/standard-multimodule/pom.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + this.group + parent-artifact + 1.7.2-SNAPSHOT + Something something parent + pom + + module1 + module2 + + +