diff --git a/.gitignore b/.gitignore
index 2c8892d8f..6d83aa177 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,7 +5,8 @@ target/
.classpath
.factorypath
.idea/
+.vscode/
.DS_Store
*.versionsBackup
/.metadata/
-*.jar
\ No newline at end of file
+*.jar
diff --git a/liberty-maven-plugin/pom.xml b/liberty-maven-plugin/pom.xml
index aca56524d..73f3455a0 100644
--- a/liberty-maven-plugin/pom.xml
+++ b/liberty-maven-plugin/pom.xml
@@ -100,7 +100,7 @@
io.openliberty.tools
ci.common
- 1.8.41
+ 1.8.42-SNAPSHOT
org.apache.commons
diff --git a/liberty-maven-plugin/src/it/dev-it/src/test/java/net/wasdev/wlp/test/dev/it/BaseDevTest.java b/liberty-maven-plugin/src/it/dev-it/src/test/java/net/wasdev/wlp/test/dev/it/BaseDevTest.java
index 676e32b57..c83b2a060 100644
--- a/liberty-maven-plugin/src/it/dev-it/src/test/java/net/wasdev/wlp/test/dev/it/BaseDevTest.java
+++ b/liberty-maven-plugin/src/it/dev-it/src/test/java/net/wasdev/wlp/test/dev/it/BaseDevTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * (c) Copyright IBM Corporation 2019, 2023.
+ * (c) Copyright IBM Corporation 2019, 2025
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -72,7 +72,7 @@ public class BaseDevTest {
final static String RUNNING_GENERATE_FEATURES = "Running liberty:generate-features";
final static String REGENERATE_FEATURES = "Regenerated the following features:";
final static String GENERATE_FEATURES = "Generated the following features:";
- final static String SERVER_XML_COMMENT = "Plugin has generated Liberty features"; // the explanation added to server.xml
+ // final static String SERVER_XML_COMMENT = "Plugin has generated Liberty features"; // the explanation added to server.xml
final static String NEW_FILE_INFO_MESSAGE = "This file was generated by the Liberty Maven Plugin and will be overwritten"; // the explanation added to the generated features file
final static String SERVER_CONFIG_SUCCESS = "CWWKZ0003I:";// CWWKZ0003I: The application xxx updated in y.yyy seconds.
final static String SERVER_UPDATE_COMPLETE = "CWWKF0008I:"; // Feature update completed in 0.649 seconds.
diff --git a/liberty-maven-plugin/src/it/dev-it/src/test/java/net/wasdev/wlp/test/dev/it/DevGenerateFeaturesDependenciesTest.java b/liberty-maven-plugin/src/it/dev-it/src/test/java/net/wasdev/wlp/test/dev/it/DevGenerateFeaturesDependenciesTest.java
index 3faf4e1e7..7aabb5a0e 100644
--- a/liberty-maven-plugin/src/it/dev-it/src/test/java/net/wasdev/wlp/test/dev/it/DevGenerateFeaturesDependenciesTest.java
+++ b/liberty-maven-plugin/src/it/dev-it/src/test/java/net/wasdev/wlp/test/dev/it/DevGenerateFeaturesDependenciesTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * (c) Copyright IBM Corporation 2022.
+ * (c) Copyright IBM Corporation 2022, 2025
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,13 +30,13 @@
public class DevGenerateFeaturesDependenciesTest extends BaseDevTest {
@BeforeClass
- public static void setUpBeforeClass() throws Exception {
- setUpBeforeClass(null, "../resources/basic-dev-project-umbrella-deps");
- }
+ public static void setUpBeforeClass() throws Exception {
+ setUpBeforeClass(null, "../resources/basic-dev-project-umbrella-deps");
+ }
@AfterClass
public static void cleanUpAfterClass() throws Exception {
- BaseDevTest.cleanUpAfterClass();
+ BaseDevTest.cleanUpAfterClass();
}
@Test
@@ -45,16 +45,17 @@ public void updateDependencyTest() throws Exception {
assertTrue(verifyLogMessageExists("Listening for transport dt_socket at address: 8077", 20000) || verifyLogMessageExists("The debug port 8077 is not available.",20000));
assertTrue(verifyLogMessageExists("Liberty is running in dev mode.", 10000));
- File generatedFeaturesFile = getGeneratedFeaturesFile();
+ //File generatedFeaturesFile = getGeneratedFeaturesFile();
File targetGeneratedFeaturesFile = getTargetGeneratedFeaturesFile();
assertTrue(pom.exists());
- assertTrue(generatedFeaturesFile.exists());
+ //assertTrue(generatedFeaturesFile.exists());
assertTrue(targetGeneratedFeaturesFile.exists());
- long lastModified = generatedFeaturesFile.lastModified();
+ // long lastModified = generatedFeaturesFile.lastModified();
+ long lastModified = targetGeneratedFeaturesFile.lastModified();
waitLongEnough();
// verify mpHealth-2.2 is in generated features file
- assertTrue(verifyLogMessageExists("mpHealth-2.2", 10000, generatedFeaturesFile));
+ assertTrue(verifyLogMessageExists("mpHealth-2.2", 10000, targetGeneratedFeaturesFile));
assertTrue(verifyLogMessageExists("mpHealth-2.2", 10000)); // should appear in the message "CWWKF0012I: The server installed the following features:"
int generateFeaturesCount = countOccurrences("Running liberty:generate-features", logFile);
@@ -77,16 +78,20 @@ public void updateDependencyTest() throws Exception {
// Dev mode should now run the generate features mojo
assertTrue(getLogTail(), verifyLogMessageExists("Generated the following features:", 15000, logFile, ++generateFeaturesCount)); // mojo ran
- assertTrue(generatedFeaturesFile.exists());
- assertTrue(getLogTail(), lastModified < generatedFeaturesFile.lastModified());
+ // assertTrue(generatedFeaturesFile.exists());
+ // assertTrue(getLogTail(), lastModified < generatedFeaturesFile.lastModified());
assertTrue(targetGeneratedFeaturesFile.exists());
+ assertTrue(getLogTail(), lastModified < targetGeneratedFeaturesFile.lastModified());
+ //assertTrue(targetGeneratedFeaturesFile.exists());
// verify that mpHealth-3.0 is now in the generated features file
- assertTrue(getLogTail(), verifyLogMessageExists("mpHealth-3.1", 10000, generatedFeaturesFile));
+ // assertTrue(getLogTail(), verifyLogMessageExists("mpHealth-3.1", 10000, generatedFeaturesFile));
+ assertTrue(getLogTail(), verifyLogMessageExists("mpHealth-3.1", 10000, targetGeneratedFeaturesFile));
assertTrue(getLogTail(), verifyLogMessageExists("mpHealth-3.1", 10000)); // should appear in the message "CWWKF0012I: The server installed the following features:"
// verify that mpHealth-2.2 is no longer in the generated features file
- assertFalse(getLogTail(), verifyLogMessageExists("mpHealth-2.2", 10000, generatedFeaturesFile));
+ // assertFalse(getLogTail(), verifyLogMessageExists("mpHealth-2.2", 10000, generatedFeaturesFile));
+ assertFalse(getLogTail(), verifyLogMessageExists("mpHealth-2.2", 10000, targetGeneratedFeaturesFile));
}
}
diff --git a/liberty-maven-plugin/src/it/dev-it/src/test/java/net/wasdev/wlp/test/dev/it/DevTest.java b/liberty-maven-plugin/src/it/dev-it/src/test/java/net/wasdev/wlp/test/dev/it/DevTest.java
index 54bcae1f1..ea6247302 100644
--- a/liberty-maven-plugin/src/it/dev-it/src/test/java/net/wasdev/wlp/test/dev/it/DevTest.java
+++ b/liberty-maven-plugin/src/it/dev-it/src/test/java/net/wasdev/wlp/test/dev/it/DevTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * (c) Copyright IBM Corporation 2019, 2022.
+ * (c) Copyright IBM Corporation 2019, 2025
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -276,7 +276,7 @@ public void generateFeatureTest() throws Exception {
int runGenerateFeaturesCount = countOccurrences(RUNNING_GENERATE_FEATURES, logFile);
int installedFeaturesCount = countOccurrences(SERVER_INSTALLED_FEATURES, logFile);
- File newFeatureFile = getGeneratedFeaturesFile();
+ // File newFeatureFile = getGeneratedFeaturesFile();
File newTargetFeatureFile = getTargetGeneratedFeaturesFile();
File serverXmlFile = new File(tempProj, "/src/main/liberty/config/server.xml");
assertTrue(serverXmlFile.exists());
@@ -293,11 +293,11 @@ public void generateFeatureTest() throws Exception {
verifyFileExists(helloBatchObj, 15000);
// ... and run the proper mojo.
assertTrue(verifyLogMessageExists(RUNNING_GENERATE_FEATURES, 10000, ++runGenerateFeaturesCount)); // mojo ran
- assertTrue(verifyFileExists(newFeatureFile, 5000)); // mojo created file
+ // assertTrue(verifyFileExists(newFeatureFile, 5000)); // mojo created file
assertTrue(verifyFileExists(newTargetFeatureFile, 5000)); // dev mode copied file
- assertTrue(verifyLogMessageExists("batch-1.0", 10000, newFeatureFile));
- assertTrue(verifyLogMessageExists(NEW_FILE_INFO_MESSAGE, 10000, newFeatureFile));
- assertTrue(verifyLogMessageExists(SERVER_XML_COMMENT, 10000, serverXmlFile));
+ assertTrue(verifyLogMessageExists("batch-1.0", 10000, newTargetFeatureFile));
+ assertTrue(verifyLogMessageExists(NEW_FILE_INFO_MESSAGE, 10000, newTargetFeatureFile));
+ // assertTrue(verifyLogMessageExists(SERVER_XML_COMMENT, 10000, serverXmlFile));
// "CWWKF0012I: The server installed the following features:" assume batch-1.0 is in there
// batch-1.0 pulls in other features that can take a long time to download.
assertTrue(verifyLogMessageExists(SERVER_INSTALLED_FEATURES, 123000, ++installedFeaturesCount));
@@ -345,7 +345,7 @@ public void generateFeatureTest() throws Exception {
writer.write("o\n"); // on optimize regenerate
writer.flush();
assertTrue(verifyLogMessageExists(GENERATE_FEATURES, 10000, logFile, ++generateFeaturesCount));
- assertTrue(verifyLogMessageExists("batch-1.0", 10000, newFeatureFile, 0)); // exist 0 times
+ assertTrue(verifyLogMessageExists("batch-1.0", 10000, newTargetFeatureFile, 0)); // exist 0 times
// Check for server response to newly generated feature list.
assertTrue(verifyLogMessageExists(SERVER_UPDATE_COMPLETE, 10000, serverUpdateCount+1));
// Need to ensure server finished updating before the next test starts.
diff --git a/liberty-maven-plugin/src/it/dev-it/src/test/java/net/wasdev/wlp/test/dev/it/MultiModuleUpdatePomsTest.java b/liberty-maven-plugin/src/it/dev-it/src/test/java/net/wasdev/wlp/test/dev/it/MultiModuleUpdatePomsTest.java
index be9d05d09..0798ad340 100644
--- a/liberty-maven-plugin/src/it/dev-it/src/test/java/net/wasdev/wlp/test/dev/it/MultiModuleUpdatePomsTest.java
+++ b/liberty-maven-plugin/src/it/dev-it/src/test/java/net/wasdev/wlp/test/dev/it/MultiModuleUpdatePomsTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * (c) Copyright IBM Corporation 2022.
+ * (c) Copyright IBM Corporation 2022, 2025
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -55,7 +55,8 @@ public void updatePomsTest() throws Exception {
int earTestsCount = countOccurrences("guide-maven-multimodules-ear tests compilation was successful.", logFile);
// verify that generated-features.xml file exists
- File newFeatureFile = getGeneratedFeaturesFile("ear");
+ // File newFeatureFile = getGeneratedFeaturesFile("ear");
+ File newFeatureFile = getTargetGeneratedFeaturesFile("ear");
assertTrue(getLogTail(), verifyFileExists(newFeatureFile, 1000));
long newFeatureFileLastModified = newFeatureFile.lastModified();
waitLongEnough();
diff --git a/liberty-maven-plugin/src/it/generate-features-it/src/test/java/net/wasdev/wlp/test/dev/it/BaseGenerateFeaturesTest.java b/liberty-maven-plugin/src/it/generate-features-it/src/test/java/net/wasdev/wlp/test/dev/it/BaseGenerateFeaturesTest.java
index f5d2c10bb..c8c44d0dc 100644
--- a/liberty-maven-plugin/src/it/generate-features-it/src/test/java/net/wasdev/wlp/test/dev/it/BaseGenerateFeaturesTest.java
+++ b/liberty-maven-plugin/src/it/generate-features-it/src/test/java/net/wasdev/wlp/test/dev/it/BaseGenerateFeaturesTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * (c) Copyright IBM Corporation 2022.
+ * (c) Copyright IBM Corporation 2022, 2025
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -63,11 +63,14 @@ public class BaseGenerateFeaturesTest {
static File targetDir;
static String processOutput = "";
static File newFeatureFile;
+ static File newFeatureFileSrc;
static File pom;
static File serverXmlFile;
static final String GENERATED_FEATURES_FILE_NAME = "generated-features.xml";
- static final String GENERATED_FEATURES_FILE_PATH = "/src/main/liberty/config/configDropins/overrides/" + GENERATED_FEATURES_FILE_NAME;
+ static final String GENERATED_FEATURES_FILE_PATH = "/target/liberty/wlp/usr/servers/defaultServer/configDropins/overrides/" + GENERATED_FEATURES_FILE_NAME;
+ static final String GENERATED_FEATURES_FILE_PATH_SRC = "/src/main/liberty/config/configDropins/overrides/" + GENERATED_FEATURES_FILE_NAME;
+ static final String SERVER_MISSING_MESSAGE = "The 'generate-features' goal requires an existing Liberty server";
protected static void setUpBeforeTest(String projectRoot) throws IOException, InterruptedException {
basicProj = new File(projectRoot);
@@ -81,6 +84,7 @@ protected static void setUpBeforeTest(String projectRoot) throws IOException, In
assertTrue(logFile.createNewFile());
newFeatureFile = new File(tempProj, GENERATED_FEATURES_FILE_PATH);
+ newFeatureFileSrc = new File(tempProj, GENERATED_FEATURES_FILE_PATH_SRC);
pom = new File(tempProj, "pom.xml");
assertTrue(pom.exists());
replaceVersion(tempProj);
@@ -223,7 +227,12 @@ protected static Set readFeatures(File configurationFile) throws Excepti
}
protected void runCompileAndGenerateFeatures() throws IOException, InterruptedException {
- runProcess("compile liberty:generate-features");
+ runProcess("clean compile liberty:create liberty:generate-features");
+ }
+
+ protected void runCompileAndGenerateFeaturesToSrc() throws IOException, InterruptedException {
+ // do not create liberty when generating to src
+ runProcess("clean compile liberty:generate-features -DgenerateToSrc=true");
}
protected void runGenerateFeaturesGoal() throws IOException, InterruptedException {
diff --git a/liberty-maven-plugin/src/it/generate-features-it/src/test/java/net/wasdev/wlp/test/dev/it/GenerateFeaturesTest.java b/liberty-maven-plugin/src/it/generate-features-it/src/test/java/net/wasdev/wlp/test/dev/it/GenerateFeaturesTest.java
index 379a66bba..53136e9e9 100644
--- a/liberty-maven-plugin/src/it/generate-features-it/src/test/java/net/wasdev/wlp/test/dev/it/GenerateFeaturesTest.java
+++ b/liberty-maven-plugin/src/it/generate-features-it/src/test/java/net/wasdev/wlp/test/dev/it/GenerateFeaturesTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * (c) Copyright IBM Corporation 2022.
+ * (c) Copyright IBM Corporation 2022, 2025
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -52,15 +52,26 @@ public void cleanUp() throws Exception {
@Test
public void basicTest() throws Exception {
- runCompileAndGenerateFeatures();
- // verify that the target directory was created
+ executeBasicTests(false);
+ }
+
+ private void executeBasicTests(boolean generateToSrc) throws Exception {
+ File featureFile;
+ if (generateToSrc) {
+ featureFile = newFeatureFileSrc;
+ runCompileAndGenerateFeaturesToSrc();
+ } else {
+ featureFile = newFeatureFile;
+ runCompileAndGenerateFeatures();
+ }
+ // verify that the target directory was created by compile goal
assertTrue(targetDir.exists());
// verify that the generated features file was created
- assertTrue(formatOutput(processOutput), newFeatureFile.exists());
+ assertTrue(formatOutput(processOutput), featureFile.exists());
// verify that the correct features are in the generated-features.xml
- Set features = readFeatures(newFeatureFile);
+ Set features = readFeatures(featureFile);
Set expectedFeatures = getExpectedGeneratedFeaturesSet();
assertEquals(expectedFeatures.size(), features.size());
assertEquals(expectedFeatures, features);
@@ -72,11 +83,26 @@ public void basicTest() throws Exception {
"\n",
serverXmlFile);
- runGenerateFeaturesGoal();
// no additional features should be generated
- assertTrue(newFeatureFile.exists());
- features = readFeatures(newFeatureFile);
- assertEquals(0, features.size());
+ if (generateToSrc) {
+ // In src dir the generated file is preserved and contains no new features
+ runCompileAndGenerateFeaturesToSrc();
+ assertTrue(featureFile.exists());
+ features = readFeatures(featureFile);
+ assertEquals(formatOutput(processOutput), 0, features.size());
+ } else {
+ // the server dir is in target directory and a mvn clean is performed so it has been removed
+ runCompileAndGenerateFeatures();
+ assertFalse(featureFile.exists()); // after clean no feature file is created
+ }
+ }
+
+ @Test
+ public void generateToSrcTest() throws Exception {
+ newFeatureFile.delete(); // delete file if it exists but do not assert
+ assertFalse(newFeatureFileSrc.exists()); // assuming no other test creates this file
+ executeBasicTests(true);
+ assertTrue(newFeatureFileSrc.delete()); // clean up the generated file
}
@Test
@@ -91,6 +117,18 @@ public void noClassFiles() throws Exception {
assertTrue(processOutput.contains(GenerateFeaturesMojo.NO_CLASSES_DIR_WARNING));
}
+ @Test
+ public void noServer() throws Exception {
+ // do not create the server before running generate-features
+ runGenerateFeaturesGoal();
+
+ // verify that generated features file was not created
+ assertFalse(newFeatureFile.exists());
+
+ // verify server not found warning message
+ assertTrue(processOutput.contains(SERVER_MISSING_MESSAGE));
+ }
+
@Test
public void customFeaturesTest() throws Exception {
// complete the setup of the test
@@ -115,7 +153,7 @@ public void customFeaturesTest() throws Exception {
@Test
public void serverXmlCommentNoFMTest() throws Exception {
// initially the expected comment is not found in server.xml
- assertFalse(verifyLogMessageExists(GenerateFeaturesMojo.FEATURES_FILE_MESSAGE, 10, serverXmlFile));
+ // assertFalse(verifyLogMessageExists(GenerateFeaturesMojo.FEATURES_FILE_MESSAGE, 10, serverXmlFile));
// also we wish to test behaviour when there is no element so test that
assertFalse(verifyLogMessageExists("", 10, serverXmlFile));
@@ -128,8 +166,8 @@ public void serverXmlCommentNoFMTest() throws Exception {
Charset charset = StandardCharsets.UTF_8;
String serverXmlContents = new String(Files.readAllBytes(serverXmlFile.toPath()), charset);
serverXmlContents = "\n" + serverXmlContents;
- assertTrue(serverXmlContents,
- verifyLogMessageExists(GenerateFeaturesMojo.FEATURES_FILE_MESSAGE, 100, serverXmlFile));
+ // assertTrue(serverXmlContents,
+ // verifyLogMessageExists(GenerateFeaturesMojo.FEATURES_FILE_MESSAGE, 100, serverXmlFile));
}
@Test
@@ -142,7 +180,7 @@ public void serverXmlCommentFMTest() throws Exception {
serverXmlFile);
// initially the expected comment is not found in server.xml
- assertFalse(verifyLogMessageExists(GenerateFeaturesMojo.FEATURES_FILE_MESSAGE, 10, serverXmlFile));
+ // assertFalse(verifyLogMessageExists(GenerateFeaturesMojo.FEATURES_FILE_MESSAGE, 10, serverXmlFile));
runCompileAndGenerateFeatures();
@@ -153,8 +191,8 @@ public void serverXmlCommentFMTest() throws Exception {
Charset charset = StandardCharsets.UTF_8;
String serverXmlContents = new String(Files.readAllBytes(serverXmlFile.toPath()), charset);
serverXmlContents = "\n" + serverXmlContents;
- assertTrue(serverXmlContents,
- verifyLogMessageExists(GenerateFeaturesMojo.FEATURES_FILE_MESSAGE, 100, serverXmlFile));
+ // assertTrue(serverXmlContents,
+ // verifyLogMessageExists(GenerateFeaturesMojo.FEATURES_FILE_MESSAGE, 100, serverXmlFile));
}
/**
diff --git a/liberty-maven-plugin/src/it/generate-features-it/src/test/java/net/wasdev/wlp/test/dev/it/MultiModuleGenerateFeaturesTest.java b/liberty-maven-plugin/src/it/generate-features-it/src/test/java/net/wasdev/wlp/test/dev/it/MultiModuleGenerateFeaturesTest.java
index 707420936..c2ee7eb92 100644
--- a/liberty-maven-plugin/src/it/generate-features-it/src/test/java/net/wasdev/wlp/test/dev/it/MultiModuleGenerateFeaturesTest.java
+++ b/liberty-maven-plugin/src/it/generate-features-it/src/test/java/net/wasdev/wlp/test/dev/it/MultiModuleGenerateFeaturesTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * (c) Copyright IBM Corporation 2022.
+ * (c) Copyright IBM Corporation 2022, 2025
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -40,6 +40,7 @@ public void setUp() throws Exception {
assertTrue(pom.exists());
replaceVersion(new File(tempProj, "pom")); // "pom" module is liberty configuration module
newFeatureFile = new File(tempProj, "pom" + GENERATED_FEATURES_FILE_PATH);
+ newFeatureFileSrc = new File(tempProj, "pom" + GENERATED_FEATURES_FILE_PATH_SRC);
serverXmlFile = new File(tempProj, "pom/src/main/liberty/config/server.xml");
targetDir = new File(tempProj, "war/target");
runProcess("install");
@@ -48,14 +49,20 @@ public void setUp() throws Exception {
@Override
protected void runCompileAndGenerateFeatures() throws IOException, InterruptedException {
- runProcess("compile io.openliberty.tools:liberty-maven-plugin:" + System.getProperty("mavenPluginVersion")
- + ":generate-features");
+ String lmp = "io.openliberty.tools:liberty-maven-plugin:" + System.getProperty("mavenPluginVersion");
+ runProcess("clean compile " + lmp + ":create " + lmp + ":generate-features");
+ }
+
+ @Override
+ protected void runCompileAndGenerateFeaturesToSrc() throws IOException, InterruptedException {
+ String lmp = "io.openliberty.tools:liberty-maven-plugin:" + System.getProperty("mavenPluginVersion");
+ runProcess("clean compile " + lmp + ":generate-features -DgenerateToSrc=true");
}
@Override
protected void runGenerateFeaturesGoal() throws IOException, InterruptedException {
runProcess("io.openliberty.tools:liberty-maven-plugin:" + System.getProperty("mavenPluginVersion")
- + ":generate-features");
+ + ":generate-features ");
}
@Override
diff --git a/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/BasicSupport.java b/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/BasicSupport.java
index 9a98d5299..1b06fcd14 100644
--- a/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/BasicSupport.java
+++ b/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/BasicSupport.java
@@ -22,9 +22,7 @@
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Enumeration;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.zip.ZipEntry;
@@ -61,8 +59,6 @@ public abstract class BasicSupport extends AbstractLibertySupport {
protected boolean defaultOutputDirSet = false;
- protected boolean skipServerConfigSetup = false;
-
/**
* Skips the specific goal
*/
@@ -206,10 +202,6 @@ protected void init() throws MojoExecutionException {
super.init();
- if (skipServerConfigSetup) {
- return;
- }
-
try {
// First check if installDirectory is set, if it is, then we can skip this
if (installDirectory != null) {
diff --git a/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/DevMojo.java b/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/DevMojo.java
index 11dca8229..f9173b59e 100644
--- a/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/DevMojo.java
+++ b/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/DevMojo.java
@@ -1,5 +1,5 @@
/**
- * (C) Copyright IBM Corporation 2019, 2026.
+ * (C) Copyright IBM Corporation 2019, 2026
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -137,8 +137,15 @@ public class DevMojo extends LooseAppSupport {
@Parameter(property = "container", defaultValue = "false")
private boolean container;
- @Parameter(property = "generateFeatures", defaultValue = "false")
- private boolean generateFeatures;
+ @Parameter(property = "generateFeatures", defaultValue = "true")
+ private boolean generateFeatures;
+
+ /**
+ * If generateToSrc is true, then create the file containing new features in the src directory
+ * Otherwise, place the file in the target directory where the Liberty server is defined.
+ */
+ @Parameter(property = "generateToSrc", defaultValue = "false")
+ private boolean generateToSrc;
/**
* Whether to recompile dependencies. Defaults to false for single module
@@ -353,7 +360,7 @@ public DevMojoUtil(File installDir, File userDir, File serverDirectory, File sou
File testSourceDirectory, File configDirectory, File projectDirectory, File multiModuleProjectDirectory,
List resourceDirs, JavaCompilerOptions compilerOptions, String mavenCacheLocation,
List upstreamProjects, List upstreamMavenProjects, boolean recompileDeps,
- File pom, Map> parentPoms, boolean generateFeatures, boolean skipInstallFeature,
+ File pom, Map> parentPoms, boolean generateFeatures, boolean generateToSrc, boolean skipInstallFeature,
Set compileArtifactPaths, Set testArtifactPaths, List webResourceDirs, File serverOutputDirectory) throws IOException, PluginExecutionException {
super(new File(project.getBuild().getDirectory()), serverDirectory, sourceDirectory, testSourceDirectory,
configDirectory, projectDirectory, multiModuleProjectDirectory, resourceDirs, changeOnDemandTestsAction, hotTests, skipTests,
@@ -361,7 +368,7 @@ public DevMojoUtil(File installDir, File userDir, File serverDirectory, File sou
((long) (compileWait * 1000L)), libertyDebug, false, false, pollingTest, container, containerfile,
containerBuildContext, containerRunOpts, containerBuildTimeout, skipDefaultPorts, compilerOptions,
keepTempContainerfile, mavenCacheLocation, upstreamProjects, recompileDeps, project.getPackaging(),
- pom, parentPoms, generateFeatures, compileArtifactPaths, testArtifactPaths, webResourceDirs, compileMojoError);
+ pom, parentPoms, generateFeatures, generateToSrc, compileArtifactPaths, testArtifactPaths, webResourceDirs, compileMojoError);
this.libertyDirPropertyFiles = LibertyPropFilesUtility.getLibertyDirectoryPropertyFiles(new CommonLogger(getLog()), installDir, userDir,
serverDirectory, serverOutputDirectory);
@@ -449,7 +456,7 @@ public void libertyCreate() throws PluginExecutionException {
}
@Override
- public boolean libertyGenerateFeatures(Collection classes, boolean optimize) {
+ public boolean libertyGenerateFeatures(Collection classes, boolean optimize, boolean genToSrc, boolean useTmpDirOut, boolean useTmpDirIn) {
try {
if (classes != null) {
Element[] classesElem = new Element[classes.size()];
@@ -459,11 +466,10 @@ public boolean libertyGenerateFeatures(Collection classes, boolean optim
i++;
}
// generate features for only the classFiles passed
- runLibertyMojoGenerateFeatures(element(name("classFiles"), classesElem), optimize);
+ runLibertyMojoGenerateFeatures(element(name("classFiles"), classesElem), optimize, genToSrc, useTmpDirOut, useTmpDirIn);
} else {
- // pass null for classFiles so that features are generated for ALL of the
- // classes
- runLibertyMojoGenerateFeatures(null, optimize);
+ // pass null for classFiles so that features are generated for ALL of the classes
+ runLibertyMojoGenerateFeatures(null, optimize, genToSrc, useTmpDirOut, useTmpDirIn);
}
return true; // successfully generated features
} catch (MojoExecutionException e) {
@@ -786,19 +792,28 @@ public boolean updateArtifactPaths(ProjectModule projectModule, boolean redeploy
// detect compile dependency changes
if (!dependencyListsEquals(getCompileDependency(deps), getCompileDependency(oldDeps))) {
+ boolean generateFeaturesSuccess = false;
// optimize generate features
if (generateFeatures) {
getLog().debug("Detected a change in the compile dependencies for "
+ buildFile + " , regenerating features");
- boolean generateFeaturesSuccess = libertyGenerateFeatures(null, true);
- if (generateFeaturesSuccess) {
- util.getJavaSourceClassPaths().clear();
+ // If generateToSrc is false then we must copy new generated features file from temp dir to server dir after install
+ generateFeaturesSuccess = optimizeGenerateFeatures(!generateToSrc, false);
+ // install new generated features because deploy will copy config files to server dir.
+ // It will not trigger install-feature if the feature list has not changed
+ util.installFeaturesToTempDir(generateFeaturesFile, generateFeaturesOutputDir, null, generateFeaturesSuccess);
+ // When generating to the src dir, mojo deploy will copy the files from src to the server.
+ // When not generating to the src dir, we must copy the generated features file here.
+ if (!generateToSrc) {
+ // Copy the file here to be used by updateExistingFeatures() below
+ util.copyGeneratedFeaturesFile(serverDirectory); // finalize the generate-features operation
}
- // install new generated features, will not trigger install-feature if the feature list has not changed
- util.installFeaturesToTempDir(generatedFeaturesFile, configDirectory, null,
- generateFeaturesSuccess);
}
runLibertyMojoDeploy();
+ // Update the features after deploy mojo has copied the config files to server dir
+ if (generateFeaturesSuccess) {
+ updateExistingFeatures(); // update the dev mode cache of features in the server
+ }
}
}
} catch (ProjectBuildingException | DependencyResolutionRequiredException | IOException
@@ -996,6 +1011,7 @@ public boolean recompileBuildFile(File buildFile, Set compileArtifactPat
boolean redeployApp = false;
boolean runBoostPackage = false;
boolean optimizeGenerateFeatures = false;
+ boolean generateFeaturesSuccess = false;
ProjectBuildingResult build;
try {
@@ -1110,19 +1126,17 @@ public boolean recompileBuildFile(File buildFile, Set compileArtifactPat
compileArtifactPaths.addAll(project.getCompileClasspathElements());
testArtifactPaths.addAll(project.getTestClasspathElements());
- boolean generateFeaturesSuccess = false;
if (optimizeGenerateFeatures && generateFeatures) {
getLog().debug("Detected a change in the compile dependencies, regenerating features");
// always optimize generate features on dependency change
- generateFeaturesSuccess = libertyGenerateFeatures(null, true);
- if (generateFeaturesSuccess) {
- util.getJavaSourceClassPaths().clear();
- } else {
+ // If generateToSrc is false then we must copy (below) new generated features file from temp dir to server dir
+ generateFeaturesSuccess = optimizeGenerateFeatures(!generateToSrc, false);
+ util.installFeaturesToTempDir(generateFeaturesFile, generateFeaturesOutputDir, null, generateFeaturesSuccess);
+ if (!generateFeaturesSuccess) {
installFeature = false; // skip installing features if generate features fails
}
-
}
-
+
// We don't currently have the ability to dynamically add new directories to be watched
// There is so much that we are dynamically able to do that this could be surprising.
// For now issue a warning
@@ -1161,9 +1175,16 @@ public boolean recompileBuildFile(File buildFile, Set compileArtifactPat
} else if (createServer) {
runLibertyMojoCreate();
} else if (redeployApp) {
- util.installFeaturesToTempDir(generatedFeaturesFile, configDirectory, null,
- generateFeaturesSuccess);
+ // Copy the file here to be used by updateExistingFeatures() below
+ // If generateToSrc is false then we must copy new generated features file from temp dir to server dir after install
+ if (generateFeaturesSuccess && !generateToSrc) {
+ util.copyGeneratedFeaturesFile(serverDirectory); // finalize the generate-features operation
+ }
runLibertyMojoDeploy();
+ // Update the features after deploy mojo has copied the config files to server dir and generated features file added
+ if (generateFeaturesSuccess) {
+ updateExistingFeatures(); // update the dev mode cache of features in the server
+ }
}
if (installFeature) {
runLibertyMojoInstallFeature(null, null, super.getContainerName());
@@ -1534,35 +1555,18 @@ private void doDevMode() throws MojoExecutionException {
getLog().info("Running boost:package");
runBoostMojo("package");
} else {
+ // If generate features to server directory then create server first.
if (generateFeatures) {
- // generate features on startup - provide all classes and only user specified
- // features to binary scanner
- try {
- String generatedFileCanonicalPath;
- try {
- generatedFileCanonicalPath = new File(configDirectory,
- BinaryScannerUtil.GENERATED_FEATURES_FILE_PATH).getCanonicalPath();
- } catch (IOException e) {
- generatedFileCanonicalPath = new File(configDirectory,
- BinaryScannerUtil.GENERATED_FEATURES_FILE_PATH).toString();
- }
- getLog().warn(
- "The source configuration directory will be modified. Features will automatically be generated in a new file: "
- + generatedFileCanonicalPath);
- runLibertyMojoGenerateFeatures(null, true);
- } catch (MojoExecutionException e) {
- if (e.getCause() != null && e.getCause() instanceof PluginExecutionException) {
- // PluginExecutionException indicates that the binary scanner jar could not be found
- getLog().error(e.getMessage() + ".\nDisabling the automatic generation of features.");
- generateFeatures = false;
- } else {
- throw new MojoExecutionException(e.getMessage()
- + " To disable the automatic generation of features, start dev mode with -DgenerateFeatures=false.",
- e);
- }
+ if (generateToSrc) {
+ generateFeaturesOnStartup();
+ runLibertyMojoCreate();
+ } else {
+ runLibertyMojoCreate();
+ generateFeaturesOnStartup();
}
+ } else {
+ runLibertyMojoCreate();
}
- runLibertyMojoCreate();
// If non-container, install features before starting server. Otherwise, user
// should have "RUN features.sh" in their Containerfile/Dockerfile if they want features to be
// installed.
@@ -1668,7 +1672,7 @@ private void doDevMode() throws MojoExecutionException {
util = new DevMojoUtil(installDirectory, userDirectory, serverDirectory, sourceDirectory, testSourceDirectory,
configDirectory, project.getBasedir(), multiModuleProjectDirectory, resourceDirs, compilerOptions,
settings.getLocalRepository(), upstreamProjects, upstreamMavenProjects, recompileDeps, pom, parentPoms,
- generateFeatures, skipInstallFeature, compileArtifactPaths, testArtifactPaths, webResourceDirs, new File(super.outputDirectory,serverName));
+ generateFeatures, generateToSrc, skipInstallFeature, compileArtifactPaths, testArtifactPaths, webResourceDirs, new File(super.outputDirectory,serverName));
} catch (IOException | PluginExecutionException |DependencyResolutionRequiredException e) {
throw new MojoExecutionException("Error initializing dev mode.", e);
}
@@ -1701,6 +1705,39 @@ private void doDevMode() throws MojoExecutionException {
}
}
+ private void generateFeaturesOnStartup() throws MojoExecutionException {
+ // generate features on startup - provide all classes and only user specified
+ // features to binary scanner
+ try {
+ String generatedFileCanonicalPath;
+ try {
+ generatedFileCanonicalPath = new File(configDirectory,
+ BinaryScannerUtil.GENERATED_FEATURES_FILE_PATH).getCanonicalPath();
+ } catch (IOException e) {
+ generatedFileCanonicalPath = new File(configDirectory,
+ BinaryScannerUtil.GENERATED_FEATURES_FILE_PATH).toString();
+ }
+ if (generateToSrc) {
+ getLog().info(
+ "The source configuration directory will be modified. Features will automatically be generated in a new file: "
+ + generatedFileCanonicalPath);
+ }
+ // During dev mode start up the server is not running yet so we will generate features to the correct
+ // output directory and then install features in the next step.
+ runLibertyMojoGenerateFeatures(null, true, generateToSrc, false, false);
+ } catch (MojoExecutionException e) {
+ if (e.getCause() != null && e.getCause() instanceof PluginExecutionException) {
+ // PluginExecutionException indicates that the binary scanner jar could not be found
+ getLog().error(e.getMessage() + ".\nDisabling the automatic generation of features.");
+ generateFeatures = false;
+ } else {
+ throw new MojoExecutionException(e.getMessage()
+ + " To disable the automatic generation of features, type 'g' and press 'Enter' once dev mode is running or restart dev mode with -DgenerateFeatures=false.",
+ e);
+ }
+ }
+ }
+
@Override
public void execute() throws MojoExecutionException {
init();
@@ -2320,7 +2357,7 @@ protected void runLibertyMojoCreate() throws MojoExecutionException {
* @throws MojoExecutionException
*/
@Override
- protected void runLibertyMojoGenerateFeatures(Element classFiles, boolean optimize) throws MojoExecutionException {
- super.runLibertyMojoGenerateFeatures(classFiles, optimize);
+ protected void runLibertyMojoGenerateFeatures(Element classFiles, boolean optimize, boolean genToSrc, boolean useTmpDirOut, boolean useTmpDirIn) throws MojoExecutionException {
+ super.runLibertyMojoGenerateFeatures(classFiles, optimize, genToSrc, useTmpDirOut, useTmpDirIn);
}
}
diff --git a/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/GenerateFeaturesMojo.java b/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/GenerateFeaturesMojo.java
index cefc8be81..d1294a319 100644
--- a/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/GenerateFeaturesMojo.java
+++ b/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/GenerateFeaturesMojo.java
@@ -1,5 +1,5 @@
/**
- * (C) Copyright IBM Corporation 2021, 2024.
+ * (C) Copyright IBM Corporation 2021, 2026
*
* 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
@@ -21,11 +21,14 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.versioning.ComparableVersion;
import org.apache.maven.execution.ProjectDependencyGraph;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.MojoExecutionException;
@@ -35,16 +38,14 @@
import org.apache.maven.project.ProjectBuildingException;
import org.apache.maven.project.ProjectBuildingResult;
import org.w3c.dom.Element;
-import org.xml.sax.SAXException;
import io.openliberty.tools.common.plugins.config.ServerConfigXmlDocument;
-import io.openliberty.tools.common.plugins.config.XmlDocument;
import io.openliberty.tools.common.plugins.util.BinaryScannerUtil;
import static io.openliberty.tools.common.plugins.util.BinaryScannerUtil.*;
import io.openliberty.tools.common.plugins.util.PluginExecutionException;
import io.openliberty.tools.common.plugins.util.ServerFeatureUtil;
import io.openliberty.tools.common.plugins.util.ServerFeatureUtil.FeaturesPlatforms;
-import io.openliberty.tools.maven.ServerFeatureSupport;
+import io.openliberty.tools.common.plugins.util.VersionUtility;
/**
* This mojo generates the features required in the featureManager element in
@@ -57,16 +58,20 @@
@Mojo(name = "generate-features", threadSafe = true)
public class GenerateFeaturesMojo extends PluginConfigSupport {
- public static final String FEATURES_FILE_MESSAGE = "The Liberty Maven Plugin has generated Liberty features necessary for your application in "
- + GENERATED_FEATURES_FILE_PATH;
public static final String HEADER = "This file was generated by the Liberty Maven Plugin and will be overwritten on subsequent runs of the liberty:generate-features goal."
- + "\n It is recommended that you do not edit this file and that you commit this file to your version control.";
+ + "\n It is recommended that you do not edit this file.";
public static final String GENERATED_FEATURES_COMMENT = "The following features were generated based on API usage detected in your application";
public static final String NO_NEW_FEATURES_COMMENT = "No additional features generated";
public static final String NO_CLASSES_DIR_WARNING = "Could not find classes directory to generate features against. Liberty features will not be generated. "
+ "Ensure your project has first been compiled.";
-
- private File binaryScanner;
+ public static final String VERSIONLESS_FEATURE_DETECTED_DEVMODE = "Versionless features are detected in the server configuration. "
+ + "If you would like to continue using the automatic generation of features, remove all versionless features from your server configuration. "
+ + "If you would like to continue using versionless features, you can disable the automatic generation of features.";
+ public static final String VERSIONLESS_FEATURE_DETECTED = "Versionless features are detected in the server configuration. "
+ + "To use the 'generate-features' goal, remove all versionless features from your server configuration. "
+ + "If you would like to continue using versionless features, you cannot use the 'generate-features' goal.";
+ private static final String OPEN_LIBERTY_PRODUCT_ID = "io.openliberty";
+ private static final String WEBSPHERE_LIBERTY_PRODUCT_ID = "com.ibm.websphere.appserver.runtime";
@Parameter(property = "classFiles")
private List classFiles;
@@ -78,16 +83,71 @@ public class GenerateFeaturesMojo extends PluginConfigSupport {
@Parameter(property = "optimize", defaultValue = "true")
private boolean optimize;
+ /**
+ * If generateToSrc is true, then create the file containing new features in the src directory
+ * Otherwise, place the file in the target directory where the Liberty server is defined.
+ */
+ @Parameter(property = "generateToSrc", defaultValue = "false")
+ private boolean generateToSrc;
+
+ /**
+ * The isDevMode parameter is for internal use only. It is not for users.
+ * This parameter must only be set to true when this mojo is called from dev mode and false otherwise.
+ * This parameter is added to support generateToSrc and to cause writing the generated features file
+ * to the server directory (if it exists) in stand-alone mode (not dev mode) in order to save the user
+ * having to copy it manually.
+ */
+ @Parameter(property = "isDevMode", defaultValue = "false")
+ private boolean isDevMode;
+
+ /**
+ * The useTempDirAsOutput parameter is for internal use only. It is not for users.
+ * The parameter is only used when generateToSrc is false meaning we generate to serverDir.
+ * It is needed in dev mode because the server is running and we need to ensure the features
+ * that are generated are installed before we update a running server.
+ * When the parameter is true we will write the generated features file to the special generate-features
+ * temp directory.
+ */
+ @Parameter(property = "useTempDirAsOutput", defaultValue = "false")
+ private boolean useTempDirAsOutput;
+
+ /**
+ * The useTempDirAsContext parameter is for internal use only. It is not for users.
+ * It is needed in dev mode when the user updates a server config file which might affect
+ * the features that will be generated. In such a case we will required the caller to copy the
+ * serverDir configuration files into the special generate-features temp directory and augment it
+ * with the file changed by the user. We do not do this all the time because of the performance
+ * cost of copying all the files.
+ * When the parameter is true we will use the generate-features temp directory as the context for
+ * feature generation.
+ */
+ @Parameter(property = "useTempDirAsContext", defaultValue = "false")
+ private boolean useTempDirAsContext;
+
+ /**
+ * Generating features is performed relative to a certain server. We only generate features
+ * that are missing from a server config. By default we generate features that are missing
+ * from the server directory in target/liberty/wlp/usr/servers/.
+ * If generateToSrc is specified then we generate features which are missing from the Liberty
+ * config specified in the src directory src/main/liberty/config.
+ * We will select one server config as the context of this operation.
+ */
+ private File generationContextDir;
+
+ /**
+ * Liberty features are generated in the context of a certain server and they are stored in
+ * an XML file in the configDir, the serverDir or a temporary directory. When using dev
+ * mode we must write to the tempDir and call install-features before we can use the
+ * features in the running server. When not using dev mode we can just write the features
+ * to the configDir or serverDir as indicated by the generateToSrc option.
+ */
+ private File generationOutputDir;
+
@Override
protected void init() throws MojoExecutionException {
- // @see io.openliberty.tools.maven.BasicSupport#init() skip server config
- // setup as generate features does not require the server to be set up install
- // dir, wlp dir, outputdir, etc.
- this.skipServerConfigSetup = true;
-
super.init();
- }
-
+ }
+
@Override
public void execute() throws MojoExecutionException {
init();
@@ -104,7 +164,7 @@ public void execute() throws MojoExecutionException {
}
/**
- * Generates features for the application given the API usage detected and
+ * Generates features for the application given the API usage detected by the binary scanner and
* taking any user specified features into account
*
* @throws MojoExecutionException
@@ -129,7 +189,7 @@ private void generateFeatures() throws MojoExecutionException, PluginExecutionEx
getLog().info("\nSkipping module " + project.getArtifactId() + " which is not configured for the generate-features goal.\n");
return;
}
-
+
List downstreamProjects = graph.getDownstreamProjects(project, true);
if (!downstreamProjects.isEmpty()) {
getLog().debug("Downstream projects: " + downstreamProjects);
@@ -156,13 +216,47 @@ private void generateFeatures() throws MojoExecutionException, PluginExecutionEx
return;
}
}
+ // Ensure server dir exists to generate features to the $serverDirectory/configDropins/overrides/generated-features.xml
+ if (!generateToSrc && !serverDirectory.exists()) {
+ throw new MojoExecutionException("The 'generate-features' goal requires an existing Liberty server in directory " + serverDirectory.getPath() + ". Please run the 'liberty:create' goal before 'generate-features'.");
+ }
+ // Detect if there is a generate-features.xml file in src already when generating to server dir
+ if (!generateToSrc && new File(configDirectory, GENERATED_FEATURES_FILE_PATH).exists()) {
+ if (isDevMode) { // this is serious because dev mode will overwrite the generated file when copying from src
+ getLog().error("A 'generated-features.xml' file was detected in the source Liberty configuration directory. " +
+ "It will overwrite the file generated to the server directory by the automatic generation of features. " +
+ "To continue to generate features to the server directory, you must delete the 'generated-features.xml' file " +
+ "in the source Liberty configuration directory. To generate features to the source Liberty configuration " +
+ "directory instead, you can type 's' + Enter.");
+ } else { // command line mojo just a warning that this configuration is not expected
+ getLog().warn("A 'generated-features.xml' file was detected in the source Liberty configuration directory. " +
+ "To generate features to the server directory, it is recommended you delete the 'generated-features.xml' " +
+ "file in the source Liberty configuration directory. To generate features to the source Liberty " +
+ "configuration directory, you must use the option -DgenerateToSrc=true.");
+ }
+ }
+
+ if (useTempDirAsContext) {
+ // When this parameter is true it is required that the caller has copied the config into this dir.
+ generationContextDir = getGeneratedFeaturesTempDir();
+ } else {
+ // The config dir is the one in the src directory. Otherwise generate for the target/liberty/wlp dir.
+ generationContextDir = generateToSrc ? configDirectory : serverDirectory;
+ }
+ // When using dev mode we always generate to a temporary directory so we can call install before writing to server dir.
+ generationOutputDir = useTempDirAsOutput ? getGeneratedFeaturesTempDir() : generationContextDir;
- binaryScanner = getBinaryScannerJarFromRepository();
- BinaryScannerHandler binaryScannerHandler = new BinaryScannerHandler(binaryScanner);
+ // The executable file used to scan binaries for the Liberty features they use.
+ File binaryScannerJar = getBinaryScannerJarFromRepository();
+ BinaryScannerHandler binaryScannerHandler = new BinaryScannerHandler(binaryScannerJar);
getLog().debug("--- Generate Features values ---");
- getLog().debug("Binary scanner jar: " + binaryScanner.getName());
+ getLog().debug("Binary scanner jar: " + binaryScannerJar.getName());
getLog().debug("optimize generate features: " + optimize);
+ getLog().debug("useTempDirAsOutput (dev mode only): " + useTempDirAsOutput);
+ getLog().debug("useTempDirAsContext (dev mode only): " + useTempDirAsContext);
+ getLog().debug("generate to directory: " + generationOutputDir.getAbsolutePath());
+
if (classFiles != null && !classFiles.isEmpty()) {
getLog().debug("Generate features for the following class files: " + classFiles.toString());
}
@@ -184,7 +278,7 @@ private void generateFeatures() throws MojoExecutionException, PluginExecutionEx
// getSpecifiedFeatures may not return the features in the correct case
// Set featuresToInstall = getSpecifiedFeatures(null);
- // get existing server features from source directory
+ // get existing server features from directory of interest
ServerFeatureUtil servUtil = getServerFeatureUtil(true, null);
Set generatedFiles = new HashSet();
@@ -209,13 +303,28 @@ private void generateFeatures() throws MojoExecutionException, PluginExecutionEx
// user specified features
getLog().warn(NO_CLASSES_DIR_WARNING);
}
- eeVersion = getEEVersion(mavenProjects);
- mpVersion = getMPVersion(mavenProjects);
+ eeVersion = getEEVersion(mavenProjects, servUtil);
+ mpVersion = getMPVersion(mavenProjects, servUtil);
String logLocation = project.getBuild().getDirectory();
String eeVersionArg = composeEEVersion(eeVersion);
String mpVersionArg = composeMPVersion(mpVersion);
- scannedFeatureList = binaryScannerHandler.runBinaryScanner(nonCustomFeatures, classFiles, directories, logLocation, eeVersionArg, mpVersionArg, optimize);
+
+ Map featureListFileMap = new HashMap();
+ String libertyGroupId = getLibertyRuntimeGroupId();
+ getLog().debug("Resolve the liberty groupId used to fetch feature lists, getLibertyRuntimeGroupId()="+libertyGroupId);
+ if (WEBSPHERE_LIBERTY_PRODUCT_ID.equals(libertyGroupId)) {
+ File baseFeatureListFile = getWebSphereFeatureListFile(FEATURE_LIST_BASE);
+ featureListFileMap.put(WSBASE_FEATURELIST_KEY, baseFeatureListFile);
+ File coreFeatureListFile = getWebSphereFeatureListFile(FEATURE_LIST_CORE);
+ featureListFileMap.put(WSCORE_FEATURELIST_KEY, coreFeatureListFile);
+ } else if (OPEN_LIBERTY_PRODUCT_ID.equals(libertyGroupId)) {
+ File featureListFile = getOpenFeatureListFile();
+ featureListFileMap.put(OL_FEATURELIST_KEY, featureListFile);
+ } // else should not happen, just pass empty map
+
+ scannedFeatureList = binaryScannerHandler.runBinaryScanner(nonCustomFeatures, classFiles, directories, logLocation,
+ eeVersionArg, mpVersionArg, featureListFileMap, optimize);
} catch (BinaryScannerUtil.NoRecommendationException noRecommendation) {
throw new MojoExecutionException(String.format(BinaryScannerUtil.BINARY_SCANNER_CONFLICT_MESSAGE3, noRecommendation.getConflicts()));
} catch (BinaryScannerUtil.FeatureModifiedException featuresModified) {
@@ -250,6 +359,8 @@ private void generateFeatures() throws MojoExecutionException, PluginExecutionEx
} catch (BinaryScannerUtil.IllegalTargetException illegalTargets) {
String messages = buildInvalidArgExceptionMessage(illegalTargets.getEELevel(), illegalTargets.getMPLevel(), eeVersion, mpVersion);
throw new MojoExecutionException(messages);
+ } catch (BinaryScannerUtil.VersionlessFeatureDetectedException versionless) {
+ throw new MojoExecutionException(isDevMode ? VERSIONLESS_FEATURE_DETECTED_DEVMODE : VERSIONLESS_FEATURE_DETECTED);
} catch (PluginExecutionException x) {
// throw an error when there is a problem not caught in runBinaryScanner()
Object o = x.getCause();
@@ -267,7 +378,7 @@ private void generateFeatures() throws MojoExecutionException, PluginExecutionEx
servUtil.setLowerCaseFeatures(false);
// get set of user defined features so they can be omitted from the generated
// file that will be written
- FeaturesPlatforms fp = servUtil.getServerFeatures(configDirectory, serverXmlFile, new HashMap(),
+ FeaturesPlatforms fp = servUtil.getServerFeatures(generationContextDir, serverXmlFile, new HashMap(),
generatedFiles);
Set userDefinedFeatures = optimize ? existingFeatures : (fp !=null) ? fp.getFeatures(): new HashSet();
getLog().debug("User defined features:" + userDefinedFeatures);
@@ -278,14 +389,20 @@ private void generateFeatures() throws MojoExecutionException, PluginExecutionEx
}
getLog().debug("Features detected by binary scanner which are not in server.xml" + missingLibertyFeatures);
- File newServerXmlSrc = new File(configDirectory, GENERATED_FEATURES_FILE_PATH);
- File serverXml = findConfigFile("server.xml", serverXmlFile);
- ServerConfigXmlDocument doc = getServerXmlDocFromConfig(serverXml);
- getLog().debug("Xml document we'll try to update after generate features doc=" + doc + " file=" + serverXml);
+ // generate the new features into an xml file in the correct context directory
+ // The ServerConfigXmlDocument class will create the directories if needed.
+ File generatedXmlFile = new File(generationOutputDir, GENERATED_FEATURES_FILE_PATH);
+
+ // For standalone goal (not dev mode) with generateToSrc=true, also write to server dir if it exists
+ boolean shouldWriteToServerDir = !isDevMode && generateToSrc && (serverDirectory != null) && serverDirectory.exists();
+ File serverDirXmlFile = null;
+ if (shouldWriteToServerDir) {
+ serverDirXmlFile = new File(serverDirectory, GENERATED_FEATURES_FILE_PATH);
+ }
try {
if (missingLibertyFeatures.size() > 0) {
- Set existingGeneratedFeatures = getGeneratedFeatures(servUtil, newServerXmlSrc);
+ Set existingGeneratedFeatures = getGeneratedFeatures(servUtil, generatedXmlFile);
if (!missingLibertyFeatures.equals(existingGeneratedFeatures)) {
// Create special XML file to contain generated features.
ServerConfigXmlDocument configDocument = ServerConfigXmlDocument.newInstance();
@@ -296,55 +413,105 @@ private void generateFeatures() throws MojoExecutionException, PluginExecutionEx
getLog().debug(String.format("Adding missing feature %s to %s.", missing, GENERATED_FEATURES_FILE_PATH));
configDocument.createFeature(missing);
}
- // Generate log message before writing file as the file change event kicks off other dev mode actions
+ // Output log messages before writing file as the file change event kicks off other dev mode actions
getLog().info("Generated the following features: " + missingLibertyFeatures);
- configDocument.writeXMLDocument(newServerXmlSrc);
- getLog().debug("Created file " + newServerXmlSrc);
- // Add a reference to this new file in existing server.xml.
- addGenerationCommentToConfig(doc, serverXml);
+ printCommonMessages(eeVersion, mpVersion, generatedXmlFile);
+ configDocument.writeXMLDocument(generatedXmlFile);
+ getLog().debug("Created file " + generatedXmlFile.getAbsolutePath());
+
+ // For standalone mode with generateToSrc=true, also write to server directory
+ if (shouldWriteToServerDir) {
+ if (writeToServerDir(configDocument, serverDirXmlFile)) {
+ getLog().debug("Also created file " + serverDirXmlFile.getAbsolutePath());
+ }
+ }
} else {
- getLog().info("Regenerated the following features: " + missingLibertyFeatures);
+ getLog().info("No change to the generated feature list: " + missingLibertyFeatures);
+ printCommonMessages(eeVersion, mpVersion, generatedXmlFile);
}
} else {
getLog().info("No additional features were generated.");
- if (newServerXmlSrc.exists()) {
+ if (generatedXmlFile.exists()) {
// generated-features.xml exists but no additional features were generated
// create empty features list with comment
ServerConfigXmlDocument configDocument = ServerConfigXmlDocument.newInstance();
configDocument.createComment(HEADER);
Element featureManagerElem = configDocument.createFeatureManager();
configDocument.createComment(featureManagerElem, NO_NEW_FEATURES_COMMENT);
- configDocument.writeXMLDocument(newServerXmlSrc);
+ configDocument.writeXMLDocument(generatedXmlFile);
+
+ // For standalone mode with generateToSrc=true, also write to server directory
+ if (shouldWriteToServerDir && serverDirXmlFile.exists()) {
+ writeToServerDir(configDocument, serverDirXmlFile);
+ }
}
}
} catch (ParserConfigurationException | TransformerException | IOException e) {
getLog().debug("Exception creating the server features file", e);
- throw new MojoExecutionException(
- "Automatic generation of features failed. Error attempting to create the "
- + GENERATED_FEATURES_FILE_NAME
- + ". Ensure your id has write permission to the server configuration directory.",
- e);
+ throw new MojoExecutionException(
+ "Automatic generation of features failed. Error attempting to create the "
+ + generatedXmlFile.getAbsolutePath()
+ + ". Ensure your ID has write permission to the directory the generated features file will be written to.",
+ e);
}
+
+ }
+
+ /*
+ * This method prints the output for both cases when features are generated (i.e., new features were generated or the same set)
+ */
+ private void printCommonMessages(String eeVersion, String mpVersion, File generatedXmlFile) {
+ getLog().info("The generated feature list has been written to the following file: " + generatedXmlFile.getAbsolutePath());
+ if (eeVersion == null && mpVersion == null) {
+ getLog().info("Verify the generated feature list meets the needs of your application." +
+ " If you see features missing or features at unexpected versions, please add the expected features to your server configuration" +
+ (isDevMode ? "." : " and run the generate-features goal again."));
+ }
+ }
+
+ /*
+ * This routine writes an xml document to the specififed file and returns false if there is an exception
+ * while writing or true otherwise.
+ */
+ private boolean writeToServerDir(ServerConfigXmlDocument configDocument, File serverDirXmlFile) {
+ try {
+ configDocument.writeXMLDocument(serverDirXmlFile);
+ } catch (TransformerException | IOException e) {
+ getLog().warn("Failed to write generated-features.xml to server directory: "
+ + serverDirXmlFile.getAbsolutePath() + ". Ensure your ID has write permission to the server configuration directory. "
+ + "Message from the Exception: " + e.getMessage());
+ return false;
+ }
+ return true;
}
- // Get the features from the server config and optionally exclude the specified config files from the search.
private Set getServerFeatures(ServerFeatureUtil servUtil, Set generatedFiles, boolean excludeGenerated) {
+ return getServerFeaturesPlatforms(servUtil, generatedFiles, excludeGenerated, true);
+ }
+ private Set getServerPlatforms(ServerFeatureUtil servUtil, Set generatedFiles, boolean excludeGenerated) {
+ return getServerFeaturesPlatforms(servUtil, generatedFiles, excludeGenerated, false); // platforms
+ }
+
+ // Get the features from the server config and optionally exclude the specified config files from the search.
+ private Set getServerFeaturesPlatforms(ServerFeatureUtil servUtil, Set generatedFiles, boolean excludeGenerated, boolean features) {
servUtil.setLowerCaseFeatures(false);
// if optimizing, ignore generated files when passing in existing features to
// binary scanner
- FeaturesPlatforms fp = servUtil.getServerFeatures(configDirectory, serverXmlFile,
+ FeaturesPlatforms fp = servUtil.getServerFeatures(generationContextDir, serverXmlFile,
new HashMap(), excludeGenerated ? generatedFiles : null); // pass generatedFiles to exclude them
servUtil.setLowerCaseFeatures(true);
if (fp == null) {
return new HashSet();
- }
- return fp.getFeatures();
+ } else if (features) {
+ return fp.getFeatures();
+ } // else
+ return fp.getPlatforms();
}
- // returns the features specified in the generated-features.xml file
+ // returns the features specified in the generated-features.xml file in the generation context directory
private Set getGeneratedFeatures(ServerFeatureUtil servUtil, File generatedFeaturesFile) {
servUtil.setLowerCaseFeatures(false);
- FeaturesPlatforms result = servUtil.getServerXmlFeatures(new FeaturesPlatforms(), configDirectory,
+ FeaturesPlatforms result = servUtil.getServerXmlFeatures(new FeaturesPlatforms(), generationContextDir,
generatedFeaturesFile, null, null);
servUtil.setLowerCaseFeatures(true);
Set features = new HashSet();
@@ -354,6 +521,11 @@ private Set getGeneratedFeatures(ServerFeatureUtil servUtil, File genera
return features;
}
+ // returns the hidden directory we use for generate-features special purposes
+ private File getGeneratedFeaturesTempDir() {
+ return new File(project.getBuild().getDirectory(), GENERATED_FEATURES_TEMP_DIR);
+ }
+
/**
* Gets the binary scanner jar file from the local cache.
* Downloads it first from connected repositories such as Maven Central if a newer release is available than the cached version.
@@ -375,51 +547,94 @@ private File getBinaryScannerJarFromRepository() throws PluginExecutionException
}
}
- private ServerConfigXmlDocument getServerXmlDocFromConfig(File serverXml) {
- if (serverXml == null || !serverXml.exists()) {
+ /*
+ * Gets the file containing the Open Liberty feature list from the local cache.
+ * Downloads it first from connected repositories such as Maven Central if a newer release is available than the cached version.
+ * Note: Maven updates artifacts daily by default based on the last updated timestamp. Users should use 'mvn -U' to force
+ * updates if needed.
+ *
+ * @return The File object of the feature list in the local cache or null if version number not available
+ */
+ private static String OPEN_LIBERTY_FEATURE_LIST_START = "25.0.0.7";
+ private File getOpenFeatureListFile() throws MojoExecutionException {
+ String libertyVersion = getLibertyRuntimeVersion();
+ if (libertyVersion == null) {
return null;
}
- try {
- return ServerConfigXmlDocument.newInstance(serverXml);
- } catch (ParserConfigurationException | SAXException | IOException e) {
- getLog().debug("Exception creating server.xml object model", e);
+ // Feature lists were first published for 25.0.0.7. For liberty releases prior to this simply use the
+ // earliest available feature list, 25.0.0.7.
+ if (VersionUtility.compareArtifactVersion(libertyVersion, OPEN_LIBERTY_FEATURE_LIST_START, true) < 0) {
+ libertyVersion = OPEN_LIBERTY_FEATURE_LIST_START;
}
- return null;
+ libertyVersion = "[" + libertyVersion + "]"; // Maven syntax to specify an exact version, not a range
+ return getArtifact(OL_FEATURELIST_GROUP_ID, OL_FEATURELIST_ARTIFACT_ID, OL_FEATURELIST_TYPE, libertyVersion).getFile();
}
- /**
- * Remove the comment in server.xml that warns we created another file with features in it.
+ /*
+ * Gets the file containing the indicated Websphere feature list from the local cache.
+ * Downloads it first from connected repositories such as Maven Central if a newer release is available than the cached version.
+ * Note: Maven updates artifacts daily by default based on the last updated timestamp. Users should use 'mvn -U' to force
+ * updates if needed.
+ *
+ * @return The File object of the feature list in the local cache.
*/
- private void removeGenerationCommentFromConfig(ServerConfigXmlDocument doc, File serverXml) {
- if (doc == null) {
- return;
+ private static String FEATURE_LIST_BASE = "base";
+ private static String FEATURE_LIST_CORE = "core";
+ private File getWebSphereFeatureListFile(String featureListVar) throws MojoExecutionException {
+ // For releases earlier than 25.0.0.7 use 25.0.0.7 Maven coordinates (batch 1).
+ // For releases 25.0.0.8 to 25.0.0.12 use batch 2 coordinates.
+ // For 26.0.0.1 and later use the third batch.
+ String libertyGroupId, libertyArtifactId;
+ String libertyVersion = getLibertyRuntimeVersion();
+ if (libertyVersion == null) {
+ return null;
}
- try {
- doc.removeFMComment(FEATURES_FILE_MESSAGE);
- doc.writeXMLDocument(serverXml);
- } catch (IOException | TransformerException e) {
- getLog().debug("Exception removing comment from server.xml", e);
+ // Publishing started with 25.0.0.7 so for any liberty <25.0.0.7 use the 07 values
+ if (VersionUtility.compareArtifactVersion(libertyVersion, WS_FEATURE_LIST_VERSION_BATCH1, true) <= 0) {
+ libertyGroupId = WS_FEATURELIST_GROUP_ID_BATCH1;
+ libertyArtifactId = (featureListVar.equals(FEATURE_LIST_BASE)) ? WS_BASE_FEATURE_LIST_ARTIFACT_ID_BATCH1 : WS_CORE_FEATURE_LIST_ARTIFACT_ID_BATCH1;
+ libertyVersion = WS_FEATURE_LIST_VERSION_BATCH1; // must set for versions before 07
+ } else if (VersionUtility.compareArtifactVersion(libertyVersion, WS_FEATURE_LIST_VERSION_BATCH3, true) < 0) {
+ // 25.0.0.8 to 25.0.0.12
+ libertyGroupId = WS_FEATURELIST_GROUP_ID_BATCH2;
+ libertyArtifactId = (featureListVar.equals(FEATURE_LIST_BASE)) ? WS_BASE_FEATURE_LIST_ARTIFACT_ID_BATCH2 : WS_CORE_FEATURE_LIST_ARTIFACT_ID_BATCH2;
+ } else { // 26.0.0.1 and up
+ libertyGroupId = WS_FEATURELIST_GROUP_ID_BATCH3;
+ libertyArtifactId = (featureListVar.equals(FEATURE_LIST_BASE)) ? WS_BASE_FEATURE_LIST_ARTIFACT_ID_BATCH3 : WS_CORE_FEATURE_LIST_ARTIFACT_ID_BATCH3;
}
- return;
+ libertyVersion = "[" + libertyVersion + "]"; // Maven syntax to specify an exact version, not a range
+ getLog().debug("WebSphere Liberty feature list coordinates, libertyGroupId="+libertyGroupId+" libertyArtifactId="+libertyArtifactId+" WS_FEATURELIST_TYPE="+WS_FEATURELIST_TYPE+" libertyVersion="+libertyVersion);
+ return getArtifact(libertyGroupId, libertyArtifactId, WS_FEATURELIST_TYPE, libertyVersion).getFile();
}
- /**
- * Add a comment to server.xml to warn them we created another file with features in it.
- * Only writes the file if the comment does not exist yet.
- */
- private void addGenerationCommentToConfig(ServerConfigXmlDocument doc, File serverXml) {
- if (doc == null) {
- return;
+ // resolve the Liberty version from one of the sources
+ private String getLibertyRuntimeVersion() throws MojoExecutionException {
+ // Check if libertyRuntimeVersion property is set (highest priority)
+ if (libertyRuntimeVersion != null && !libertyRuntimeVersion.isEmpty()) {
+ return libertyRuntimeVersion;
+ } else if (assemblyArtifact != null) {
+ // Resolve the artifact to get its version
+ Artifact artifact = getResolvedArtifact(assemblyArtifact);
+ if (artifact != null) {
+ return artifact.getVersion();
+ }
}
- try {
- if (doc.createFMComment(FEATURES_FILE_MESSAGE)) {
- doc.writeXMLDocument(serverXml);
- XmlDocument.addNewlineBeforeFirstElement(serverXml);
+ return null;
+ }
+
+ // resolve the Liberty GroupId from one of the sources
+ private String getLibertyRuntimeGroupId() throws MojoExecutionException {
+ // Check if libertyRuntimeGroupId property is set (highest priority)
+ if (libertyRuntimeGroupId != null && !libertyRuntimeGroupId.isEmpty()) {
+ return libertyRuntimeGroupId;
+ } else if (assemblyArtifact != null) {
+ // Resolve the artifact to get its values
+ Artifact artifact = getResolvedArtifact(assemblyArtifact);
+ if (artifact != null) {
+ return artifact.getGroupId();
}
- } catch (IOException | TransformerException e) {
- getLog().debug("Exception adding comment to server.xml", e);
}
- return;
+ return null;
}
// Return a list containing the classes directory of the Maven projects (upstream projects and main project)
@@ -462,36 +677,36 @@ private String getClassesDirectory(String outputDir) {
* @return the latest version of EE detected across multiple project modules,
* null if an EE version is not found or the version number is out of range
*/
- public String getEEVersion(List mavenProjects) {
+ private static final String JAKARTA_PLATFORM_NAME="jakartaee-"; // jakartaee-10.0 etc.
+ private static final String JAVAEE_PLATFORM_NAME="javaee-"; // javaee-7.0 etc.
+ public String getEEVersion(List mavenProjects, ServerFeatureUtil servUtil) {
String eeVersion = null;
if (mavenProjects != null) {
Set eeVersionsDetected = new HashSet();
for (MavenProject mavenProject : mavenProjects) {
- try {
- String ver = getEEVersion(mavenProject);
- getLog().debug("Java and/or Jakarta EE umbrella dependency found in project: " + mavenProject.getName());
- if (ver != null) {
- eeVersionsDetected.add(ver);
- }
- } catch (NoUmbrellaDependencyException e) {
- // umbrella dependency does not exist, do nothing
- }
- }
- if (!eeVersionsDetected.isEmpty()) {
- eeVersion = eeVersionsDetected.iterator().next();
- // if multiple EE versions are found across multiple modules, return the latest version
- for (String ver : eeVersionsDetected) {
- if (ver.compareTo(eeVersion) > 0) {
- eeVersion = ver;
- }
+ String ver = getEEVersion(mavenProject);
+ getLog().debug("Java and/or Jakarta EE umbrella dependency version: " + ver + " found in project: " + mavenProject.getName());
+ if (ver != null) {
+ eeVersionsDetected.add(ver);
}
}
+ eeVersion = findMaxVersion(eeVersionsDetected);
if (eeVersionsDetected.size() > 1) {
- getLog().debug(
- "Multiple Java and/or Jakarta EE versions found across multiple project modules, using the latest version ("
- + eeVersion + ") found to generate Liberty features.");
+ getLog().info("Multiple Java EE and/or Jakarta EE versions found across multiple project modules, using the latest version (" +
+ eeVersion + ") found to generate Liberty features.");
}
}
+ // if the dependencies do not indicate the Jakarta version then reference the platform specified in server.xml
+ // E.g. pom may specify jakarta.persistence:jakarta.persistence-api:2.2.3 to compile but does not specify Jakarta 9.1
+ if (eeVersion == null) {
+ Set platformVersions = new HashSet();
+ // Gather all Jakarta EE platform versions
+ platformVersions.addAll(getAllPlatformVersions(JAKARTA_PLATFORM_NAME, servUtil));
+ // Gather all Java EE platform versions
+ platformVersions.addAll(getAllPlatformVersions(JAVAEE_PLATFORM_NAME, servUtil));
+ // Find the maximum version from all platforms
+ eeVersion = findMaxVersion(platformVersions);
+ }
return eeVersion;
}
@@ -502,22 +717,57 @@ public String getEEVersion(List mavenProjects) {
*
* @param project the MavenProject to search
* @return EE major version corresponding to the EE umbrella dependency
- * @throws NoUmbrellaDependencyException indicates that the umbrella dependency was not found
*/
- private String getEEVersion(MavenProject project) throws NoUmbrellaDependencyException {
- if (project != null) {
- List dependencies = project.getDependencies();
- for (Dependency d : dependencies) {
- if (!d.getScope().equals("provided")) {
- continue;
- }
- if ((d.getGroupId().equals("javax") && d.getArtifactId().equals("javaee-api")) ||
- (d.getGroupId().equals("jakarta.platform") && d.getArtifactId().equals("jakarta.jakartaee-api"))) {
- return d.getVersion();
+ private String getEEVersion(MavenProject project) {
+ if (project == null) {
+ return null;
+ }
+ List dependencies = project.getDependencies();
+ Set eeVersionsDetected = new HashSet();
+ for (Dependency d : dependencies) {
+ String scope = d.getScope();
+ String groupId = d.getGroupId();
+ String artifactId = d.getArtifactId();
+ if (scope == null || groupId == null || artifactId == null) {
+ continue;
+ }
+ if (!scope.equals("provided") && !scope.equals("compile") && !scope.equals("import")) {
+ continue;
+ }
+ if ((groupId.equals("javax") && artifactId.equals("javaee-api")) ||
+ (groupId.equals("jakarta.platform") &&
+ (artifactId.equals("jakarta.jakartaee-api") ||
+ artifactId.equals("jakarta.jakartaee-web-api") ||
+ artifactId.equals("jakarta.jakartaee-core-api") ||
+ artifactId.equals("jakarta.jakartaee-bom") ||
+ artifactId.equals("jakartaee-api-parent")))) {
+ eeVersionsDetected.add(d.getVersion());
+ }
+ }
+ return findMaxVersion(eeVersionsDetected);
+ }
+
+ // Find the highest version number in the set of strings
+ // returns null if the set of strings is empty
+ private String findMaxVersion(Set versionsDetected) {
+ String maxVersion = null;
+ if (!versionsDetected.isEmpty()) {
+ maxVersion = versionsDetected.iterator().next();
+ if (versionsDetected.size() == 1) {
+ return maxVersion;
+ }
+ ComparableVersion cMaxVersion = new ComparableVersion(maxVersion);
+ // if multiple EE/MP versions are found across multiple modules, return the latest version
+ for (String ver : versionsDetected) {
+ ComparableVersion cVer = new ComparableVersion(ver);
+ getLog().debug("GenerateFeraturesMojo.findMaxVersion, ver=" + ver);
+ if (cVer.compareTo(cMaxVersion) > 0) {
+ maxVersion = ver;
+ cMaxVersion = cVer;
}
}
}
- throw new NoUmbrellaDependencyException();
+ return maxVersion;
}
/**
@@ -527,36 +777,28 @@ private String getEEVersion(MavenProject project) throws NoUmbrellaDependencyExc
* @return the latest version of MP detected across multiple project modules,
* null if an MP version is not found or the version number is out of range
*/
- public String getMPVersion(List mavenProjects) {
+ private static final String MP_PLATFORM_NAME="microProfile-"; // microProfile-7.0 etc.
+ public String getMPVersion(List mavenProjects, ServerFeatureUtil servUtil) {
String mpVersion = null;
if (mavenProjects != null) {
Set mpVersionsDetected = new HashSet();
for (MavenProject mavenProject : mavenProjects) {
- try {
- String ver = getMPVersion(mavenProject);
- getLog().debug("MicroProfile umbrella dependency found in project: " + mavenProject.getName());
- if (ver != null) {
- mpVersionsDetected.add(ver);
- }
- } catch (NoUmbrellaDependencyException e) {
- // umbrella dependency does not exist, do nothing
- }
- }
- if (!mpVersionsDetected.isEmpty()) {
- mpVersion = mpVersionsDetected.iterator().next();
- // if multiple MP versions are found across multiple modules, return the latest version
- for (String ver : mpVersionsDetected) {
- if (ver.compareTo(mpVersion) > 0) {
- mpVersion = ver;
- }
+ String ver = getMPVersion(mavenProject);
+ getLog().debug("MicroProfile umbrella dependency version: " + ver + " found in project: " + mavenProject.getName());
+ if (ver != null) {
+ mpVersionsDetected.add(ver);
}
}
+ // if multiple MP versions are found across multiple modules, return the latest version
+ mpVersion = findMaxVersion(mpVersionsDetected);
if (mpVersionsDetected.size() > 1) {
- getLog().debug(
- "Multiple MicroProfile versions found across multiple project modules, using the latest version ("
- + mpVersion + ") found to generate Liberty features.");
+ getLog().info("Multiple MicroProfile versions found across multiple project modules, using the latest version (" +
+ mpVersion + ") found to generate Liberty features.");
}
}
+ if (mpVersion == null) {
+ mpVersion = findMaxVersion(getAllPlatformVersions(MP_PLATFORM_NAME, servUtil));
+ }
return mpVersion;
}
@@ -567,24 +809,39 @@ public String getMPVersion(List mavenProjects) {
*
* @param project the MavenProject to search
* @return MP exact version code corresponding to the MP umbrella dependency
- * @throws NoUmbrellaDependencyException indicates that the umbrella dependency was not found
*/
- public String getMPVersion(MavenProject project) throws NoUmbrellaDependencyException { // figure out correct level of MP from declared dependencies
- if (project != null) {
- List dependencies = project.getDependencies();
- for (Dependency d : dependencies) {
- if (!d.getScope().equals("provided")) {
- continue;
- }
- if (d.getGroupId().equals("org.eclipse.microprofile") &&
- d.getArtifactId().equals("microprofile")) {
- return d.getVersion();
- }
+ public String getMPVersion(MavenProject project) {
+ if (project == null) {
+ return null;
+ }
+ List dependencies = project.getDependencies();
+ for (Dependency d : dependencies) {
+ if (!d.getScope().equals("provided")) {
+ continue;
+ }
+ if (d.getGroupId().equals("org.eclipse.microprofile") &&
+ d.getArtifactId().equals("microprofile")) {
+ return d.getVersion();
+ }
+ }
+ return null;
+ }
+
+ // Retrieve all platforms from the server.xml and related files that match the platform specified.
+ // Platforms have the format jakartaee-10.0 or microProfile-7.1. Return all version numbers (10.0, 7.1, etc.) that match.
+ private Set getAllPlatformVersions(String platformName, ServerFeatureUtil servUtil) {
+ Set platformVersions = new HashSet();
+ Set platforms = getServerPlatforms(servUtil, null, false);
+ for (String p : platforms) {
+ getLog().debug("GenerateFeaturesMojo.getAllPlatformVersions, searching for platform:" + platformName + " platform=" + p);
+ if (p.startsWith(platformName)) {
+ platformVersions.add(p.substring(platformName.length()));
}
}
- throw new NoUmbrellaDependencyException();
+ return platformVersions;
}
+
// Define the logging functions of the binary scanner handler and make it available in this plugin
private class BinaryScannerHandler extends BinaryScannerUtil {
BinaryScannerHandler(File scannerFile) {
@@ -622,12 +879,4 @@ private MavenProject getMavenProject(File buildFile) throws ProjectBuildingExcep
session.getProjectBuildingRequest().setResolveDependencies(true));
return build.getProject();
}
-
- /**
- * Class to indicate that an umbrella dependency was not found in the build file
- */
- public class NoUmbrellaDependencyException extends Exception {
- private static final long serialVersionUID = 1L;
- }
-
}
diff --git a/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/StartDebugMojoSupport.java b/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/StartDebugMojoSupport.java
index 7931950c0..948f7b44e 100644
--- a/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/StartDebugMojoSupport.java
+++ b/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/StartDebugMojoSupport.java
@@ -1,5 +1,5 @@
/**
- * (C) Copyright IBM Corporation 2014, 2025.
+ * (C) Copyright IBM Corporation 2014, 2026
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -242,12 +242,16 @@ protected void runLibertyMojoInstallFeature(Element features, File serverDir, St
runLibertyMojo("install-feature", config);
}
- protected void runLibertyMojoGenerateFeatures(Element classFiles, boolean optimize) throws MojoExecutionException {
+ protected void runLibertyMojoGenerateFeatures(Element classFiles, boolean optimize, boolean generateToSrc, boolean useTmpDirOut, boolean useTmpDirIn) throws MojoExecutionException {
Xpp3Dom config = ExecuteMojoUtil.getPluginGoalConfig(getLibertyPlugin(), "generate-features", getLog());
if (classFiles != null) {
config = Xpp3Dom.mergeXpp3Dom(configuration(classFiles), config);
}
+ config.addChild(element(name("isDevMode"), "true").toDom());
config.addChild(element(name("optimize"), Boolean.toString(optimize)).toDom());
+ config.addChild(element(name("generateToSrc"), Boolean.toString(generateToSrc)).toDom());
+ config.addChild(element(name("useTempDirAsOutput"), Boolean.toString(useTmpDirOut)).toDom());
+ config.addChild(element(name("useTempDirAsContext"), Boolean.toString(useTmpDirIn)).toDom());
runLibertyMojo("generate-features", config);
}