From e32528e4789d7ed68d15386e8dcd11ed1d3e9b69 Mon Sep 17 00:00:00 2001 From: Paul Gooderham Date: Thu, 13 Aug 2026 13:57:19 -0400 Subject: [PATCH 1/4] Add method to parse a loose application xml file Signed-off-by: Paul Gooderham --- .../plugins/config/ServerConfigDocument.java | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/openliberty/tools/common/plugins/config/ServerConfigDocument.java b/src/main/java/io/openliberty/tools/common/plugins/config/ServerConfigDocument.java index 2ae4372f3..df3a36bd3 100644 --- a/src/main/java/io/openliberty/tools/common/plugins/config/ServerConfigDocument.java +++ b/src/main/java/io/openliberty/tools/common/plugins/config/ServerConfigDocument.java @@ -202,7 +202,7 @@ public ServerConfigDocument(CommonLoggerI log, File originalServerXMLFile, Map springBootAppNodeLocat this.springBootAppNodeLocation = springBootAppNodeLocation; } + /** + * Parses a loose application XML file and returns a list of all sourceOnDisk + * attribute values found on {@code } and {@code } elements. + * + * @param looseAppFile - the loose application XML file to parse + * @return a List of sourceOnDisk path strings; empty if none are found or the file cannot be parsed + * @throws FileNotFoundException if the file does not exist + * @throws IOException if the file cannot be read + */ + public static Set getSourceOnDiskPaths(File looseAppFile) throws FileNotFoundException, IOException { + Set result = new HashSet(); + Document doc; + try (FileInputStream is = new FileInputStream(looseAppFile)) { + doc = getDocumentBuilder().parse(is); + } catch (SAXException e) { + return result; // not valid XML + } + for (String tag : new String[]{"file", "dir"}) { + NodeList nodes = doc.getElementsByTagName(tag); + for (int i = 0; i < nodes.getLength(); i++) { + org.w3c.dom.Node attr = nodes.item(i).getAttributes().getNamedItem("sourceOnDisk"); + if (attr != null && !attr.getNodeValue().isEmpty()) { + result.add(attr.getNodeValue()); + } + } + } + return result; + } } \ No newline at end of file From c349d3aecead8ce68e094c580837fb95c19802c9 Mon Sep 17 00:00:00 2001 From: Paul Gooderham Date: Thu, 13 Aug 2026 14:13:13 -0400 Subject: [PATCH 2/4] Parse the loose config xml file to scan Signed-off-by: Paul Gooderham --- .../plugins/util/FeatureGeneratorUtil.java | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/openliberty/tools/common/plugins/util/FeatureGeneratorUtil.java b/src/main/java/io/openliberty/tools/common/plugins/util/FeatureGeneratorUtil.java index c6d031ec2..a82197385 100644 --- a/src/main/java/io/openliberty/tools/common/plugins/util/FeatureGeneratorUtil.java +++ b/src/main/java/io/openliberty/tools/common/plugins/util/FeatureGeneratorUtil.java @@ -16,6 +16,7 @@ package io.openliberty.tools.common.plugins.util; import java.io.File; +import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.MalformedURLException; @@ -26,6 +27,8 @@ import java.util.Map; import java.util.Set; +import io.openliberty.tools.common.plugins.config.ServerConfigDocument; + public abstract class FeatureGeneratorUtil { public static final String FEATURE_GEN_MAVEN_GROUP_ID = "com.ibm.websphere.appmod.tools"; @@ -135,6 +138,7 @@ public FeatureGeneratorUtil(File featureGen) { * @param currentFeatureSet - the features already specified in the server configuration * @param classFiles - a set of class files for the generator to handle. Should be a subset of allClassesDirectories * @param allClassesDirectories - the directories containing all the class files of the application + * @param looseConfigFilePath - the absolute path to the xml config of the loose application * @param logLocation - directory name relative to project or absolute path passed to feature generator * @param targetJavaEE - generate features valid for the indicated version of EE * @param targetMicroProfile - generate features valid for the indicated version of MicroProfile @@ -155,7 +159,7 @@ public FeatureGeneratorUtil(File featureGen) { * generator when used in combination with each other. E.g. EE 7 and MP 2.1 */ public Set runFeatureGenerator(Set currentFeatureSet, List classFiles, Set allClassesDirectories, - String logLocation, String targetJavaEE, String targetMicroProfile, Map featureListFileMap, boolean optimize) + String looseConfigFilePath, String logLocation, String targetJavaEE, String targetMicroProfile, Map featureListFileMap, boolean optimize) throws PluginExecutionException, NoRecommendationException, RecommendationSetException, FeatureModifiedException, FeatureUnavailableException, IllegalTargetException, IllegalTargetComboException, VersionlessFeatureDetectedException { Set generatedFeatureList = null; @@ -167,7 +171,8 @@ public Set runFeatureGenerator(Set currentFeatureSet, List binaryInputs = getBinaryInputs(classFiles, allClassesDirectories, optimize); + Set binaryInputs = getBinaryInputs(classFiles, allClassesDirectories, looseConfigFilePath, optimize); + String logLevel; if (isDebugEnabled()) { logLevel = "*=FINE"; // generate messages for debugging by support team @@ -384,9 +389,23 @@ private Method getGeneratorMethod() throws MalformedURLException, ClassNotFoundE return featureGenMethod; } - private static Set getBinaryInputs(List classFiles, Set classDirectories, boolean optimize) throws PluginExecutionException { + private static Set getBinaryInputs(List classFiles, Set classDirectories, String looseConfigFilePath, boolean optimize) throws PluginExecutionException { Set resultSet; if (optimize) { + // Use either the loose app config or the class directories + if (looseConfigFilePath != null) { + try { + File looseAppFile = new File(looseConfigFilePath); + if (looseAppFile.exists()) { + resultSet = ServerConfigDocument.getSourceOnDiskPaths(looseAppFile); + if (!resultSet.isEmpty()) { + return resultSet; + } + } + } catch (IOException e) { + // if the app config is invalid try the class directories instead + } + } if (classDirectories == null || classDirectories.isEmpty()) { return new HashSet(); } From 16ff36a0a027be36ef1bb33a4d77b4cc0009a850 Mon Sep 17 00:00:00 2001 From: Paul Gooderham Date: Thu, 13 Aug 2026 14:16:19 -0400 Subject: [PATCH 3/4] Update comment for triggerUpstreamModuleCompile() Signed-off-by: Paul Gooderham --- .../java/io/openliberty/tools/common/plugins/util/DevUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java b/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java index a9e8b2b6c..96860eb91 100644 --- a/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java +++ b/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java @@ -5878,7 +5878,7 @@ protected void triggerMainModuleCompile(boolean testsOnly) throws IOException { /** * Trigger a compile of the entire specified module. This is only used in a - * multi-module scenario. Adds all Java files to the to be compiled list so that + * multi-module scenario. Adds all Java files to the to-be-compiled list so that * they will be compiled on next watch loop. * * @param project ProjectModule, the module to be compiled From 67840acbeac92f97820ff4becce900740b4e27a3 Mon Sep 17 00:00:00 2001 From: Paul Gooderham Date: Fri, 14 Aug 2026 18:31:42 -0400 Subject: [PATCH 4/4] Display a warning message if the loose app file is not found Signed-off-by: Paul Gooderham --- .../tools/common/plugins/util/FeatureGeneratorUtil.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/openliberty/tools/common/plugins/util/FeatureGeneratorUtil.java b/src/main/java/io/openliberty/tools/common/plugins/util/FeatureGeneratorUtil.java index a82197385..98f17568d 100644 --- a/src/main/java/io/openliberty/tools/common/plugins/util/FeatureGeneratorUtil.java +++ b/src/main/java/io/openliberty/tools/common/plugins/util/FeatureGeneratorUtil.java @@ -389,7 +389,7 @@ private Method getGeneratorMethod() throws MalformedURLException, ClassNotFoundE return featureGenMethod; } - private static Set getBinaryInputs(List classFiles, Set classDirectories, String looseConfigFilePath, boolean optimize) throws PluginExecutionException { + private Set getBinaryInputs(List classFiles, Set classDirectories, String looseConfigFilePath, boolean optimize) throws PluginExecutionException { Set resultSet; if (optimize) { // Use either the loose app config or the class directories @@ -405,6 +405,7 @@ private static Set getBinaryInputs(List classFiles, Set } catch (IOException e) { // if the app config is invalid try the class directories instead } + warn("Application descriptor file not found while generating features, using class files instead: " + looseConfigFilePath); } if (classDirectories == null || classDirectories.isEmpty()) { return new HashSet();