From f43c47711e7df64e4f038edf6a083cbe04fb9880 Mon Sep 17 00:00:00 2001 From: Arun Venmany Date: Wed, 8 Apr 2026 09:55:32 +0530 Subject: [PATCH 1/9] prepare config initial commit --- README.md | 1 + docs/prepare-config.md | 264 ++++++++++++++++++ .../it/prepare-config-it/invoker.properties | 3 + .../src/it/prepare-config-it/pom.xml | 65 +++++ .../main/liberty/config/bootstrap.properties | 4 + .../src/main/liberty/config/server.xml | 17 ++ .../src/main/webapp/index.html | 11 + .../wlp/maven/test/app/PrepareConfigIT.java | 165 +++++++++++ .../tools/maven/server/PrepareConfigMojo.java | 133 +++++++++ 9 files changed, 663 insertions(+) create mode 100644 docs/prepare-config.md create mode 100644 liberty-maven-plugin/src/it/prepare-config-it/invoker.properties create mode 100644 liberty-maven-plugin/src/it/prepare-config-it/pom.xml create mode 100644 liberty-maven-plugin/src/it/prepare-config-it/src/main/liberty/config/bootstrap.properties create mode 100644 liberty-maven-plugin/src/it/prepare-config-it/src/main/liberty/config/server.xml create mode 100644 liberty-maven-plugin/src/it/prepare-config-it/src/main/webapp/index.html create mode 100644 liberty-maven-plugin/src/it/prepare-config-it/src/test/java/net/wasdev/wlp/maven/test/app/PrepareConfigIT.java create mode 100644 liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/PrepareConfigMojo.java diff --git a/README.md b/README.md index 4d04cb183..a145f8306 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,7 @@ The Liberty Maven Plugin provides the following goals. | [install-server](docs/install-server.md#install-server) | Installs the Liberty runtime. This goal is implicitly invoked by all the other plugin goals and usually does not need to be executed explicitly. | | [java-dump](docs/java-dump.md#java-dump) | Dump diagnostic information from the server JVM. | | [package](docs/package.md#package) | Package a Liberty server. | +| [prepare-config](docs/prepare-config.md#prepare-config) | Prepare Liberty configuration and generate liberty-plugin-config.xml without creating the server or installing Liberty. Useful for IDE and language server support. | | [prepare-feature](docs/prepare-feature.md#prepare-feature) | Prepare a user feature for installation to the Liberty runtime. | | [run](docs/run.md#run) | Start a Liberty server in the foreground. The run goal implicitly creates the server, installs features referenced by the server.xml file, and deploys the application before starting the Liberty server. | | [start](docs/start.md#start) | Start a Liberty server in the background. The server instance will be automatically created if it does not exist. | diff --git a/docs/prepare-config.md b/docs/prepare-config.md new file mode 100644 index 000000000..733f5270b --- /dev/null +++ b/docs/prepare-config.md @@ -0,0 +1,264 @@ +## prepare-config + +--- + +Prepare Liberty configuration and generate `liberty-plugin-config.xml` without creating the server or installing Liberty. This lightweight goal evaluates project configuration and generates metadata needed by IDE tools and language servers. + +This goal is particularly useful for: +- Enabling IDE support for Liberty configuration files (server.xml, bootstrap.properties, server.env) +- Providing configuration metadata to language servers for code completion and diagnostics +- Supporting Liberty Tools and other IDE extensions +- Quick configuration validation without full project build + +The goal does NOT install Liberty or create a server. It only generates the configuration metadata file based on your project's Maven configuration. + +--- + +### Usage + +The `prepare-config` goal is typically used in IDE scenarios where you need configuration metadata before building the project: + +```xml + + io.openliberty.tools + liberty-maven-plugin + 3.12.1-SNAPSHOT + + + prepare-config + initialize + + prepare-config + + + + +``` + +Or run it directly from the command line: + +```bash +mvn liberty:prepare-config +``` + +--- + +### Configuration Parameters + +The `prepare-config` goal supports the following configuration parameter in addition to the [common parameters](common-parameters.md) and [common server parameters](common-server-parameters.md): + +| Parameter | Description | Required | Default | +| --------- | ----------- | -------- | ------- | +| includeServerInfo | Whether to include server-specific information in the generated config. When `true`, includes `server.xml`, `bootstrap.properties`, `jvm.options`, etc. When `false`, only includes project and build metadata. | No | `true` | + +--- + +### Examples + +#### Example 1: Basic usage (default) + +Generate configuration metadata with server information: + +```bash +mvn liberty:prepare-config +``` + +This will create `target/liberty-plugin-config.xml` with project metadata, dependencies, and configuration file references. + +#### Example 2: Generate minimal configuration + +Generate only project metadata without server-specific information: + +```bash +mvn liberty:prepare-config -DincludeServerInfo=false +``` + +This creates a minimal configuration file with just project and build information, and executes faster. + +#### Example 3: IDE integration + +Configure the goal to run automatically during project initialization: + +```xml + + io.openliberty.tools + liberty-maven-plugin + 3.12.1-SNAPSHOT + + + prepare-config-on-init + initialize + + prepare-config + + + true + + + + +``` + +--- + +### Generated Configuration File + +The `prepare-config` goal generates `target/liberty-plugin-config.xml` containing: + +**Always included:** +- Install directory (if Liberty is already installed) +- Server name and directory paths +- Project type (packaging) +- Active build profiles +- Project compile dependencies +- Aggregator parent information (for multi-module projects) + +**Included when `includeServerInfo=true`:** +- Configuration directory +- Server configuration file (`server.xml`) +- Bootstrap properties file (`bootstrap.properties`) +- JVM options file (`jvm.options`) +- Server environment file (`server.env`) +- Applications directory (`apps` or `dropins`) +- Loose application configuration +- Strip version settings +- Application filename + +--- + +### Use Cases + +#### 1. IDE Language Server Support + +IDEs using Liberty language servers can invoke this goal to get configuration metadata: + +```bash +mvn liberty:prepare-config +``` + +The generated `liberty-plugin-config.xml` provides language servers with information needed to offer: +- Code completion for Liberty configuration +- Validation of server configuration +- Quick fixes and diagnostics +- Custom file path resolution (for non-standard locations) + +**For full variable resolution features**, first install Liberty using `liberty:create`: + +```bash +mvn liberty:create +mvn liberty:prepare-config +``` + +#### 2. CI/CD Pipeline Validation + +Validate Liberty configuration early in the pipeline without full build: + +```bash +mvn liberty:prepare-config +# Parse and validate liberty-plugin-config.xml +``` + +#### 3. Multi-Module Project Setup + +For multi-module projects, run at the parent level to prepare configuration for all modules: + +```bash +mvn liberty:prepare-config -pl :module-name +``` + +#### 4. Pre-Build Configuration Analysis + +Analyze project configuration before committing to a full build: + +```bash +mvn liberty:prepare-config +# Analyze target/liberty-plugin-config.xml for issues +``` + +--- + +### Comparison with Other Goals + +| Goal | Liberty Install | Server Creation | Config Files Copied | Use Case | +|------|----------------|-----------------|---------------------|----------| +| `prepare-config` | No | No | No | Generate config metadata for tools | +| `create` | Yes | Yes | Yes | Create and configure Liberty server | +| `install-server` | Yes | No | No | Install Liberty runtime only | +| `dev` | Yes | Yes | Yes | Development mode with hot reload | + +--- + +### Performance Considerations + +The `prepare-config` goal is designed to be lightweight and fast: + +- **Typical execution time**: ~1-2 seconds +- **No Liberty download**: Does not download or install Liberty +- **No server creation**: Does not create server directories +- **Minimal I/O**: Only reads Maven configuration and writes one XML file + +This makes it ideal for IDE integration where responsiveness is critical. + +--- + +### Language Server Integration + +The `prepare-config` goal is designed to work with two Liberty language servers: + +#### 1. lemminx-liberty (XML Language Server) +Provides features for `server.xml` and related XML configuration files: +- Feature completion and validation +- Configuration element completion +- Variable resolution (requires Liberty installation) +- Hover documentation + +#### 2. liberty-ls (Properties Language Server) +Provides features for `bootstrap.properties` and `server.env` files: +- Property name completion +- Property value validation +- Custom file path detection + +**Note**: For full variable resolution in `server.xml`, Liberty must be installed first using `liberty:create` or `liberty:dev`. The `prepare-config` goal will include the Liberty installation paths in the generated config if Liberty is already present. + +--- + +### Troubleshooting + +#### Configuration file not generated + +Check that the goal executed successfully: + +```bash +mvn liberty:prepare-config -X +``` + +The debug output will show the exact location where the file is being written. + +#### Missing server information + +If server-specific information is missing, ensure `includeServerInfo=true`: + +```bash +mvn liberty:prepare-config -DincludeServerInfo=true +``` + +#### Variable resolution not working in IDE + +Variable resolution requires Liberty to be installed. Run: + +```bash +mvn liberty:create +mvn liberty:prepare-config +``` + +This will install Liberty and generate the config file with Liberty installation paths. + +--- + +### See Also + +- [Common Parameters](common-parameters.md) +- [Common Server Parameters](common-server-parameters.md) +- [create goal](create.md) - Install Liberty and create server +- [install-server goal](install-server.md) - Install Liberty runtime only +- [dev goal](dev.md) - Development mode with hot reload \ No newline at end of file diff --git a/liberty-maven-plugin/src/it/prepare-config-it/invoker.properties b/liberty-maven-plugin/src/it/prepare-config-it/invoker.properties new file mode 100644 index 000000000..7458bc25b --- /dev/null +++ b/liberty-maven-plugin/src/it/prepare-config-it/invoker.properties @@ -0,0 +1,3 @@ +# Integration test configuration for prepare-config goal +invoker.goals=initialize +invoker.maven.version=3.6.0+ \ No newline at end of file diff --git a/liberty-maven-plugin/src/it/prepare-config-it/pom.xml b/liberty-maven-plugin/src/it/prepare-config-it/pom.xml new file mode 100644 index 000000000..10011114b --- /dev/null +++ b/liberty-maven-plugin/src/it/prepare-config-it/pom.xml @@ -0,0 +1,65 @@ + + + 4.0.0 + + net.wasdev.wlp.maven.test + prepare-config-it + 1.0-SNAPSHOT + war + + + UTF-8 + 1.8 + 1.8 + + + + + jakarta.platform + jakarta.jakartaee-web-api + 9.1.0 + provided + + + org.eclipse.microprofile + microprofile + 5.0 + pom + provided + + + + + ${project.artifactId} + + + @project.groupId@ + @project.artifactId@ + @project.version@ + + + prepare-config + initialize + + prepare-config + + + testServer + true + + + + + + ${runtimeGroupId} + ${runtimeArtifactId} + ${runtimeVersion} + zip + + + + + + \ No newline at end of file diff --git a/liberty-maven-plugin/src/it/prepare-config-it/src/main/liberty/config/bootstrap.properties b/liberty-maven-plugin/src/it/prepare-config-it/src/main/liberty/config/bootstrap.properties new file mode 100644 index 000000000..eba4e17f3 --- /dev/null +++ b/liberty-maven-plugin/src/it/prepare-config-it/src/main/liberty/config/bootstrap.properties @@ -0,0 +1,4 @@ +# Bootstrap properties for prepare-config integration test +default.http.port=9080 +default.https.port=9443 +app.context.root=/ \ No newline at end of file diff --git a/liberty-maven-plugin/src/it/prepare-config-it/src/main/liberty/config/server.xml b/liberty-maven-plugin/src/it/prepare-config-it/src/main/liberty/config/server.xml new file mode 100644 index 000000000..a3ed01dfd --- /dev/null +++ b/liberty-maven-plugin/src/it/prepare-config-it/src/main/liberty/config/server.xml @@ -0,0 +1,17 @@ + + + + + jakartaee-9.1 + microProfile-5.0 + + + + + + + + + \ No newline at end of file diff --git a/liberty-maven-plugin/src/it/prepare-config-it/src/main/webapp/index.html b/liberty-maven-plugin/src/it/prepare-config-it/src/main/webapp/index.html new file mode 100644 index 000000000..aec4700e9 --- /dev/null +++ b/liberty-maven-plugin/src/it/prepare-config-it/src/main/webapp/index.html @@ -0,0 +1,11 @@ + + + + + Prepare Config Test + + +

Prepare Config Integration Test

+

This is a test application for the prepare-config goal.

+ + \ No newline at end of file diff --git a/liberty-maven-plugin/src/it/prepare-config-it/src/test/java/net/wasdev/wlp/maven/test/app/PrepareConfigIT.java b/liberty-maven-plugin/src/it/prepare-config-it/src/test/java/net/wasdev/wlp/maven/test/app/PrepareConfigIT.java new file mode 100644 index 000000000..8f9d4f9d7 --- /dev/null +++ b/liberty-maven-plugin/src/it/prepare-config-it/src/test/java/net/wasdev/wlp/maven/test/app/PrepareConfigIT.java @@ -0,0 +1,165 @@ +/** + * (C) Copyright IBM Corporation 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 the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.wasdev.wlp.maven.test.app; + +import java.io.File; +import java.io.FileInputStream; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathFactory; + +import org.junit.Assert; +import org.junit.Test; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; + +/** + * Integration test for prepare-config goal + */ +public class PrepareConfigIT { + + private static final String PLUGIN_CONFIG_XML = "target/liberty-plugin-config.xml"; + + @Test + public void testPluginConfigXmlExists() throws Exception { + File configFile = new File(PLUGIN_CONFIG_XML); + Assert.assertTrue("liberty-plugin-config.xml should exist", configFile.exists()); + } + + @Test + public void testPluginConfigXmlContainsServerName() throws Exception { + String serverName = getXPathValue("/liberty-plugin-config/serverName"); + Assert.assertEquals("Server name should be testServer", "testServer", serverName); + } + + @Test + public void testPluginConfigXmlContainsProjectType() throws Exception { + String projectType = getXPathValue("/liberty-plugin-config/projectType"); + Assert.assertEquals("Project type should be war", "war", projectType); + } + + @Test + public void testPluginConfigXmlContainsConfigFile() throws Exception { + String configFile = getXPathValue("/liberty-plugin-config/configFile"); + Assert.assertNotNull("Config file should be present", configFile); + Assert.assertTrue("Config file should reference server.xml", configFile.contains("server.xml")); + } + + @Test + public void testPluginConfigXmlContainsBootstrapPropertiesFile() throws Exception { + String bootstrapFile = getXPathValue("/liberty-plugin-config/bootstrapPropertiesFile"); + Assert.assertNotNull("Bootstrap properties file should be present", bootstrapFile); + Assert.assertTrue("Bootstrap file should reference bootstrap.properties", + bootstrapFile.contains("bootstrap.properties")); + } + + @Test + public void testPluginConfigXmlContainsServerDirectory() throws Exception { + String serverDirectory = getXPathValue("/liberty-plugin-config/serverDirectory"); + Assert.assertNotNull("Server directory should be present", serverDirectory); + } + + @Test + public void testPluginConfigXmlContainsInstallDirectory() throws Exception { + String installDirectory = getXPathValue("/liberty-plugin-config/installDirectory"); + Assert.assertNotNull("Install directory should be present", installDirectory); + } + + @Test + public void testPluginConfigXmlContainsLooseApplication() throws Exception { + String looseApp = getXPathValue("/liberty-plugin-config/looseApplication"); + Assert.assertNotNull("Loose application setting should be present", looseApp); + } + + @Test + public void testPluginConfigXmlContainsAppsDirectory() throws Exception { + String appsDir = getXPathValue("/liberty-plugin-config/appsDirectory"); + Assert.assertNotNull("Apps directory should be present", appsDir); + // Should be "apps" since we have application configured in server.xml + Assert.assertEquals("Apps directory should be 'apps'", "apps", appsDir); + } + + @Test + public void testPluginConfigXmlContainsDependencies() throws Exception { + NodeList dependencies = getXPathNodeList("/liberty-plugin-config/projectCompileDependency"); + Assert.assertNotNull("Dependencies should be present", dependencies); + Assert.assertTrue("Should have at least one dependency", dependencies.getLength() > 0); + + // Check for Jakarta EE dependency + boolean foundJakartaEE = false; + for (int i = 0; i < dependencies.getLength(); i++) { + String dep = dependencies.item(i).getTextContent(); + if (dep.contains("jakarta.jakartaee-web-api")) { + foundJakartaEE = true; + break; + } + } + Assert.assertTrue("Should contain Jakarta EE dependency", foundJakartaEE); + } + + @Test + public void testPluginConfigXmlContainsApplicationFilename() throws Exception { + String appFilename = getXPathValue("/liberty-plugin-config/applicationFilename"); + Assert.assertNotNull("Application filename should be present", appFilename); + Assert.assertTrue("Application filename should be prepare-config-it.war.xml (loose app)", + appFilename.equals("prepare-config-it.war.xml")); + } + + @Test + public void testNoTempDirectoryLeftBehind() throws Exception { + // Check that no temporary Liberty installation directories are left behind + File tempDir = new File(System.getProperty("java.io.tmpdir")); + File[] tempLibertyDirs = tempDir.listFiles((dir, name) -> + name.startsWith("liberty-temp-") && name.endsWith("-install")); + + if (tempLibertyDirs != null) { + Assert.assertEquals("No temporary Liberty directories should remain", + 0, tempLibertyDirs.length); + } + } + + /** + * Helper method to get XPath value from the plugin config XML + */ + private String getXPathValue(String expression) throws Exception { + File configFile = new File(PLUGIN_CONFIG_XML); + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc = builder.parse(new FileInputStream(configFile)); + + XPathFactory xPathfactory = XPathFactory.newInstance(); + XPath xpath = xPathfactory.newXPath(); + + return xpath.evaluate(expression, doc); + } + + /** + * Helper method to get XPath NodeList from the plugin config XML + */ + private NodeList getXPathNodeList(String expression) throws Exception { + File configFile = new File(PLUGIN_CONFIG_XML); + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc = builder.parse(new FileInputStream(configFile)); + + XPathFactory xPathfactory = XPathFactory.newInstance(); + XPath xpath = xPathfactory.newXPath(); + + return (NodeList) xpath.evaluate(expression, doc, XPathConstants.NODESET); + } +} \ No newline at end of file diff --git a/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/PrepareConfigMojo.java b/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/PrepareConfigMojo.java new file mode 100644 index 000000000..e78af78b0 --- /dev/null +++ b/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/PrepareConfigMojo.java @@ -0,0 +1,133 @@ +/** + * (C) Copyright IBM Corporation 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 the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.openliberty.tools.maven.server; + +import java.io.File; +import java.io.IOException; +import java.text.MessageFormat; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; + +/** + * Prepare Liberty configuration and generate liberty-plugin-config.xml without + * creating the server. This lightweight goal evaluates project configuration + * and generates metadata needed by IDE tools and language servers. + * + *

+ * This goal is designed to be fast and non-invasive, making it suitable for + * automatic execution when Liberty configuration files are opened in an IDE. + *

+ * + *

+ * The generated liberty-plugin-config.xml file contains: + *

+ * + * + *

+ * Parameters: + *

+ * + * + *

+ * Note: This goal does NOT install Liberty or create a server. If you need + * Liberty installed for full variable resolution in language servers, use the + * liberty:create or liberty:dev goals first. + *

+ * + *

+ * Usage Examples: + *

+ * + *
+ * {@code
+ * 
+ * mvn liberty:prepare-config
+ * 
+ * 
+ * mvn liberty:prepare-config -DincludeServerInfo=false
+ * 
+ * 
+ * mvn liberty:create
+ * mvn liberty:prepare-config
+ * }
+ * 
+ */ +@Mojo(name = "prepare-config", requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, threadSafe = true) +public class PrepareConfigMojo extends PluginConfigSupport { + + /** + * Whether to include server-specific information in the generated config. + * When true, includes server.xml, bootstrap.properties, jvm.options, etc. + * When false, only includes project and build metadata. + */ + @Parameter(property = "includeServerInfo", defaultValue = "true") + private boolean includeServerInfo; + + @Override + public void execute() throws MojoExecutionException { + init(); + + if (skip) { + getLog().info("\nSkipping prepare-config goal.\n"); + return; + } + + doPrepareConfig(); + } + + private void doPrepareConfig() throws MojoExecutionException { + getLog().info("Preparing Liberty configuration..."); + + try { + // Generate the liberty-plugin-config.xml file + File configFile = exportParametersToXml(includeServerInfo); + getLog().info(MessageFormat.format("Liberty configuration file generated: {0}", + configFile.getAbsolutePath())); + + } catch (IOException | ParserConfigurationException | TransformerException e) { + throw new MojoExecutionException("Error preparing Liberty configuration.", e); + } + } + + /** + * Override to prevent server creation during config preparation. + * This goal only generates the config file, it does not install Liberty or create a server. + */ + @Override + protected void installServerAssembly() throws MojoExecutionException { + // Only export parameters, don't install Liberty or create server + try { + exportParametersToXml(false); + // Skip the actual Liberty installation + } catch (IOException | ParserConfigurationException | TransformerException e) { + throw new MojoExecutionException("Error exporting parameters.", e); + } + } +} \ No newline at end of file From 2ff611edd2e8cb5ad2cc9e2528216f1f02c9ab2a Mon Sep 17 00:00:00 2001 From: Arun Venmany Date: Fri, 10 Apr 2026 13:39:33 +0530 Subject: [PATCH 2/9] prepare config updated design commit --- docs/prepare-config-complete-flow.md | 255 ++++++++++++++ docs/prepare-config-scenarios.md | 326 ++++++++++++++++++ docs/prepare-config.md | 56 ++- .../wlp/maven/test/app/PrepareConfigIT.java | 71 +++- .../tools/maven/server/PrepareConfigMojo.java | 103 +++++- 5 files changed, 784 insertions(+), 27 deletions(-) create mode 100644 docs/prepare-config-complete-flow.md create mode 100644 docs/prepare-config-scenarios.md diff --git a/docs/prepare-config-complete-flow.md b/docs/prepare-config-complete-flow.md new file mode 100644 index 000000000..bbd5a1ac7 --- /dev/null +++ b/docs/prepare-config-complete-flow.md @@ -0,0 +1,255 @@ +# Liberty Config Language Server Integration - Complete Flow + +## Overview +This document describes the integration between Liberty Maven/Gradle plugins and the Liberty Config Language Server (LCLS), focusing on the prepare-config goal/task and on-demand configuration generation. + +## Metadata Used by LCLS from liberty-plugin-config.xml + +### lemminx-liberty (Language Server) +- **installDirectory** - Load Liberty runtime metadata for variable resolution +- **serverDirectory** - Locate server.xml and related config files +- **userDirectory** - Access shared configuration files +- **serverOutputDirectory** - Resolve output directory variables +- **configFile** - Custom server.xml location, used for non-standard paths + +### liberty-ls (Legacy Language Server) +- **serverEnv** - Custom server.env file path +- **bootstrapPropertiesFile** - Custom bootstrap.properties file path +- **configDirectory** - Configuration directory location + +--- + +## Part 1: Liberty Plugin Flow (Maven/Gradle) + +### Command Execution +```bash +# Maven +mvn liberty:prepare-config + +# Gradle +gradle libertyPrepareConfig +``` + +### Step 1: Plugin Invocation +User or IDE executes prepare-config command. Plugin framework initializes the goal/task and reads configuration from pom.xml (Maven) or build.gradle (Gradle). + +### Step 2: Goal/Task Initialization +Reads build configuration and initializes plugin parameters: +- `includeServerInfo = true` (default) - Controls whether to include server-specific information +- `serverName` from configuration (default: "defaultServer") +- `installDirectory`, `configDirectory`, `userDirectory`, `outputDirectory` from configuration +- Validates skip parameter, exits early if skip is true + +### Step 3: Create Mock Liberty Server Structure +Creates directory structure in build output folder: +- **Maven**: Creates `target/tmp/wlp/usr/servers/{serverName}/` directory structure +- **Gradle**: Creates `build/tmp/wlp/usr/servers/{serverName}/` directory structure +- Mimics actual Liberty server layout without installing Liberty +- All directories created with proper permissions +- Returns the mock server directory path for subsequent operations + +### Step 4: Copy Configuration Files to Mock Server +Uses parent class `copyConfigFiles()` method which handles: +- Copies all files from configured `configDirectory` if specified +- Uses explicit file overrides (serverXmlFile, jvmOptionsFile, bootstrapPropertiesFile, serverEnvFile) +- Applies inline bootstrap properties and JVM options if configured in build file +- Executes `mergeServerEnv` logic if configured +- Resolves build property references (Maven: `${property}`, Gradle: `${property}`) in all config files +- Processes `` elements in server.xml +- Temporarily sets `serverDirectory` to mock location before copying, then restores original value + +### Step 5: Read Project Information +Extract project metadata from build configuration: +- Reads project coordinates: `groupId`, `artifactId`, `version`, `packaging` type +- Collects all compile-scope and runtime-scope dependencies with their coordinates +- Identifies active build profiles (Maven) or configurations (Gradle) + +### Step 6: Generate Configuration XML + +Builds mock directory paths and saves original directory values for restoration. Overrides directories to point to mock structure: +- **Maven**: Sets `installDirectory` to `target/tmp/wlp` +- **Gradle**: Sets `installDirectory` to `build/tmp/wlp` +- Sets `userDirectory` to `{buildDir}/tmp/wlp/usr` +- Sets `serverDirectory` to `{buildDir}/tmp/wlp/usr/servers/{serverName}` +- Sets `outputDirectory` to `{buildDir}/tmp/wlp/usr/servers` + +Creates XML document with root element ``: + +**Always-Included Elements:** +- ``, ``, ``, `` - All point to mock structure +- ``, ``, ``, `` - Project metadata + +**Server-Specific Information (if includeServerInfo=true):** +- ``, ``, ``, ``, `` +- ``, ``, `` + +Calls parent implementation with mock directories, then restores original directory values. + +### Step 7: Write Configuration File +Writes XML content to build output directory: +- **Maven**: `target/liberty-plugin-config.xml` +- **Gradle**: `build/liberty-plugin-config.xml` + +Closes file handles and verifies file was written successfully. + +### Step 8: Completion +Logs success messages and returns success status to build tool. + +--- + +## Part 2: Liberty Config Language Server (LCLS) Flow + +### A. Extension Startup + +#### Step 1: Initialize Extension +LemMinX (XML Language Server) starts and loads Liberty extension. Registers completion, hover, diagnostics, code actions, and document link participants. + +#### Step 2: Discover Workspaces +LibertyProjectsManager receives workspace folders from IDE, recursively searches for XML files with `` root element, detects multi-module Maven and Gradle projects, and creates LibertyWorkspace instances. + +#### Step 3: Initialize Variables Map +Calls SettingsService to initialize empty variables map. Map is created but not populated with values yet. Actual variable population happens on-demand when needed. This ensures map is never null when accessed later. + +#### Step 4: Start File Monitoring +FileWatchService monitors workspace directories for changes to server.xml, bootstrap.properties, server.env, jvm.options, and files referenced via `` elements. When changes detected, triggers variable reload for affected workspace. + +### B. File Open Detection (Diagnostics Trigger) + +#### Step 1: File Open Event +User opens server.xml or any Liberty config file in IDE. LemMinX triggers LibertyDiagnosticParticipant `doDiagnostics()` method. + +#### Step 2: Trigger Automatic Config Generation +Gets workspace folder for the opened document. Checks if project has Liberty Maven or Gradle plugin configured in build file. Calls `needsConfigGeneration()` to determine if regeneration is needed. If needed, executes prepare-config command with 5-second timeout. + +#### Step 3: Check for Configuration File (needsConfigGeneration) +Performs four checks to determine if config generation is needed: + +**Check 1: Config file doesn't exist** +- **Maven**: Looks for liberty-plugin-config.xml in target directory +- **Gradle**: Looks for liberty-plugin-config.xml in build directory +- If not found, removes project from processed cache and returns true + +**Check 2: Config points to mock server but mock directory missing** +- Reads config file and verifies mock directory exists +- **Maven**: Checks for `target/tmp/wlp/usr/servers/{serverName}` directory +- **Gradle**: Checks for `build/tmp/wlp/usr/servers/{serverName}` directory +- If missing (e.g., after clean), returns true + +**Check 3: Already processed in this session** +- Checks if project path exists in processedProjects set +- If found, returns false to skip generation + +**Check 4: Config is stale (older than build file)** +- Compares timestamps of liberty-plugin-config.xml and build file +- **Maven**: Compares with pom.xml timestamp +- **Gradle**: Compares with build.gradle or build.gradle.kts timestamp +- If config older, returns true + +If all checks pass, returns false indicating no generation needed. + +#### Step 4: Execute Config Generation +Detects build tool and constructs appropriate command: +- **Maven**: `mvn liberty:prepare-config` +- **Gradle**: `./gradlew libertyPrepareConfig` or `gradlew.bat libertyPrepareConfig` (Windows) + +Creates ProcessBuilder, executes command, captures output, and checks exit code. Returns ConfigGenerationResult with success status, error message, and duration. + +#### Step 5: Handle Generation Result + +**If Successful:** +- Config file now exists with mock server structure created +- Adds project to processedProjects set +- **Immediately calls SettingsService to reload variables for this workspace** +- Variables are populated from newly generated config +- Proceeds with full diagnostic features + +**If Failed:** +- Creates `.libertyls/prepare-config` directory and saves detailed error log to `build.log` +- Marks project as failed with error message and log file path +- Proceeds to show failure diagnostic to user + +**If Timeout (>5 seconds):** +- Allows generation process to continue in background +- Config will be checked again on next diagnostic trigger or variable access + +#### Step 6: Reload Variables After Successful Generation +After successful config generation, calls SettingsService `populateVariablesForWorkspace()` method. Reads newly generated liberty-plugin-config.xml, extracts directory paths, creates ServerConfigDocument, loads default Liberty properties and custom properties from bootstrap.properties, and stores variables in map. + +#### Step 7: Show Failure Diagnostic (If Generation Failed) +If project marked as failed, creates warning diagnostic at line 1 of server.xml with error summary, resolution steps specific to Maven or Gradle, and clickable link to detailed error log at `.libertyls/prepare-config/build.log`. + +**Maven Resolution Steps:** +1. Ensure liberty-maven-plugin version 3.11 or later +2. Run 'mvn liberty:prepare-config' manually +3. Check pom.xml configuration +4. Verify Maven is installed + +**Gradle Resolution Steps:** +1. Ensure liberty-gradle-plugin version 3.11 or later +2. Run 'gradle libertyPrepareConfig' manually +3. Check build.gradle configuration +4. Verify Gradle is installed + +#### Step 8: Perform Standard Diagnostics +After handling config generation, proceeds with standard Liberty diagnostics: validates XML structure, checks feature names, validates variable references, checks file paths in `` elements, validates configuration values, and checks for deprecated features. All diagnostics use data from mock server structure. + +### C. Variable Access (On-Demand Population) + +#### Step 1: Variable Access Trigger +Any language server feature that needs variables triggers this flow: code completion for `${variable}` references, hover documentation, validation of variable references, or document link resolution. + +#### Step 2: Get Variables for Server XML +Receives server.xml URI, calls LibertyProjectsManager to get workspace folder, checks if variables exist for workspace, and returns cached variables or empty properties. + +#### Step 3: Populate Variables If Needed +Called when variables don't exist for workspace: +- Initializes variables map if null (lazy initialization) +- Calls `ensureConfigIsUpToDate()` to check if regeneration needed +- Reads config file from build output directory (target or build) +- Extracts directory paths (installDirectory, serverDirectory, userDirectory, serverOutputDirectory) +- Creates ServerConfigDocument with extracted paths (all pointing to mock structure) +- Loads default Liberty properties and custom properties from bootstrap.properties in mock server +- Stores variables in map with workspace URI as key + +#### Step 4: Return Variables +Returns properties object with all variables for workspace. Calling feature uses variables for completion, hover, or validation. + +### D. File Change Detection (FileWatchService) + +FileWatchService monitors changes to Liberty configuration files in two locations: + +**1. Source Config Directory** (`src/main/liberty/config/`): +- Monitors for ANY file changes in the source config directory +- When change detected, automatically triggers prepare-config to sync changes to mock server +- After successful regeneration, immediately reloads variables from updated mock server +- Provides seamless developer experience - changes take effect automatically + +**2. Mock Server Directory** (`target/tmp/wlp/usr/servers/{serverName}/` or `build/tmp/wlp/usr/servers/{serverName}/`): +- Monitors for changes to config files in the mock server +- When change detected, reloads variables without regenerating config +- Handles cases where mock server is modified directly + +**Flow when source config file changes:** +1. FileWatchService detects file modification in `src/main/liberty/config/` +2. Triggers prepare-config goal/task to regenerate mock server +3. Maven/Gradle plugin copies updated files from source to mock server +4. Resolves build properties in updated files +5. Generates new liberty-plugin-config.xml +6. Immediately calls SettingsService `populateVariablesForWorkspace()` method +7. Re-reads config and configuration files from updated mock server +8. Updates variable values in workspace variable map +9. Next diagnostic run uses updated values - no IDE restart required + +--- + +## Benefits + +✅ **Zero configuration** - Works immediately after project clone without setup +✅ **Always up-to-date** - Config checked before every use, never stale +✅ **Self-healing** - Automatically fixes stale or missing configs +✅ **No Liberty installation required** - Mock server structure eliminates dependency +✅ **Simpler architecture** - No file watcher complexity for config file +✅ **No race conditions** - Synchronous on-demand checks +✅ **Easier to debug** - Straightforward flow without async events +✅ **Less resource usage** - No continuous monitoring overhead +✅ **Build tool agnostic** - Works with both Maven and Gradle projects diff --git a/docs/prepare-config-scenarios.md b/docs/prepare-config-scenarios.md new file mode 100644 index 000000000..eff77b19a --- /dev/null +++ b/docs/prepare-config-scenarios.md @@ -0,0 +1,326 @@ +# Liberty Config Language Server - Common Scenarios + +This document describes common scenarios when using the Liberty Config Language Server with Maven or Gradle projects. + +--- + +## Scenario 1: First Time Opening server.xml + +### Maven Project +1. User opens server.xml in IDE for the first time +2. LemMinX triggers diagnostics +3. Check: liberty-plugin-config.xml doesn't exist in target directory +4. Execute: `mvn liberty:prepare-config` in background with 5-second timeout +5. Maven Plugin executes: + - Creates `target/tmp/wlp/usr/servers/defaultServer/` directory structure + - Copies server.xml from `src/main/liberty/config/` to mock server + - Copies bootstrap.properties to mock server if exists + - Resolves Maven properties in all config files + - Generates `target/liberty-plugin-config.xml` with paths to mock server +6. Config generation completes successfully within timeout +7. Immediately reload variables for this workspace: + - SettingsService reads paths from config (all point to target/tmp) + - Creates ServerConfigDocument with mock server paths + - Loads default Liberty properties from metadata + - Loads custom variables from bootstrap.properties in mock server + - Stores variables in map for this workspace +8. Diagnostics run with full language server features enabled +9. User sees in IDE: + - Code completion for features, variables, file paths + - Hover documentation for elements and attributes + - Error and warning diagnostics for invalid configuration + - Clickable file paths in include elements + - All features work immediately without manual intervention + +### Gradle Project +Same flow as Maven, but: +- Check: liberty-plugin-config.xml doesn't exist in build directory +- Execute: `gradle libertyPrepareConfig` or `./gradlew libertyPrepareConfig` +- Creates `build/tmp/wlp/usr/servers/defaultServer/` directory structure +- Generates `build/liberty-plugin-config.xml` with paths to mock server + +--- + +## Scenario 2: After Clean Build + +### Maven Project (mvn clean) +1. User executes: `mvn clean` from terminal or IDE +2. Maven deletes entire target directory including: + - `target/tmp/` (mock server structure with all config files) + - `target/liberty-plugin-config.xml` (generated config file) +3. User opens server.xml in IDE +4. LemMinX triggers diagnostics +5. Check: liberty-plugin-config.xml doesn't exist in target directory +6. Check: Mock server directory missing +7. Remove project from processedProjects cache: + - Allows regeneration even though previously successful + - Ensures fresh generation after clean +8. Execute: `mvn liberty:prepare-config` +9. Maven Plugin recreates everything from scratch: + - Creates `target/tmp/wlp/usr/servers/defaultServer/` directory + - Copies server.xml from source to mock server + - Copies bootstrap.properties from source to mock server + - Copies all other config files from source to mock server + - Resolves Maven properties in all copied files + - Generates `target/liberty-plugin-config.xml` with mock paths +10. Immediately reload variables after successful generation: + - Reads newly generated config file + - Extracts directory paths pointing to new mock server + - Loads variables from new bootstrap.properties + - Stores variables in map for workspace +11. Full language server features restored automatically +12. User continues working with no manual intervention required +13. All features work as if clean never happened + +### Gradle Project (gradle clean) +Same flow as Maven, but: +- Gradle deletes entire build directory +- Check: liberty-plugin-config.xml doesn't exist in build directory +- Execute: `gradle libertyPrepareConfig` or `./gradlew libertyPrepareConfig` +- Creates `build/tmp/wlp/usr/servers/defaultServer/` directory +- Generates `build/liberty-plugin-config.xml` with mock paths + +--- + +## Scenario 3: After Build File Change + +### Maven Project (pom.xml modified) +1. User modifies pom.xml: + - Adds new dependency + - Changes plugin configuration + - Updates Liberty version + - Modifies server name or directories +2. User saves pom.xml (now newer than liberty-plugin-config.xml) +3. User opens server.xml in IDE +4. LemMinX triggers diagnostics +5. Check: liberty-plugin-config.xml exists in target directory +6. Check: Is config stale (older than pom.xml)? + - Result: YES (pom.xml modified timestamp is newer than config timestamp) +7. Execute: `mvn liberty:prepare-config` to regenerate with updated settings +8. Maven Plugin regenerates config: + - Recreates mock server structure + - Copies config files with updated Maven properties resolved + - Generates new liberty-plugin-config.xml with updated dependencies and settings +9. Immediately reload variables after successful generation: + - Reads new config file + - Extracts updated directory paths + - Loads updated variables from new bootstrap.properties + - Updates variable map for workspace +10. Diagnostics run with updated configuration and variables +11. User sees updated features reflecting new dependencies and configuration +12. No manual intervention required - all automatic + +### Gradle Project (build.gradle modified) +Same flow as Maven, but: +- User modifies build.gradle or build.gradle.kts +- Check: Is config stale (older than build.gradle)? +- Execute: `gradle libertyPrepareConfig` or `./gradlew libertyPrepareConfig` +- Gradle Plugin regenerates config with updated settings + +--- + +## Scenario 4: Config Generation Fails + +### Maven Project +1. User opens server.xml in IDE +2. LemMinX triggers diagnostics +3. Check: liberty-plugin-config.xml doesn't exist in target directory +4. Execute: `mvn liberty:prepare-config` +5. Maven Plugin fails due to: + - Invalid pom.xml syntax + - Missing liberty-maven-plugin configuration + - Incorrect plugin version + - Network error downloading dependencies + - Missing required parameters +6. Capture error output from Maven command: + - Reads all output lines from process + - Captures both standard output and error streams + - Stores complete output for logging +7. Save detailed error log to `.libertyls/prepare-config/build.log`: + - Creates `.libertyls/prepare-config` directory if doesn't exist + - Writes timestamp to log file + - Writes full Maven output to log file + - Writes error messages to log file + - Writes stack traces if available to log file +8. Mark project as failed in LibertyConfigGenerationService: + - Stores error message in failed projects map + - Stores log file path in failed projects map + - Associates failure with workspace URI +9. Warning diagnostic shown in server.xml at line 1: + ``` + Liberty configuration generation failed. Full language server features + are unavailable until this is resolved. + + Error: [Brief error summary from Maven output] + + Resolution steps: + 1. Ensure liberty-maven-plugin version 3.11 or later + 2. Run 'mvn liberty:prepare-config' manually to see full error + 3. Check pom.xml configuration + 4. Verify Maven is installed and accessible + + [View detailed error log](.libertyls/prepare-config/build.log) + ``` +10. User clicks "View detailed error log" link in diagnostic +11. IDE opens `.libertyls/prepare-config/build.log` in editor +12. User reads full error details: + - Sees complete Maven output + - Identifies specific error (e.g., missing plugin version in pom.xml) + - Understands what needs to be fixed +13. User fixes issue in pom.xml (e.g., adds correct plugin version) +14. User saves pom.xml (now newer than failed config attempt) +15. User reopens server.xml or triggers diagnostics +16. Config check detects stale config (pom.xml newer than last attempt) +17. Regenerates config successfully this time: + - Maven plugin executes without errors + - Creates mock server structure + - Generates config file +18. Reloads variables immediately after successful generation +19. Project removed from failed projects list +20. Warning diagnostic automatically disappears from server.xml +21. Full language server features now available +22. User continues working normally + +### Gradle Project +Same flow as Maven, but: +- Execute: `gradle libertyPrepareConfig` or `./gradlew libertyPrepareConfig` +- Gradle Plugin fails due to similar reasons (invalid build.gradle, missing plugin, etc.) +- Warning diagnostic shows Gradle-specific resolution steps: + ``` + Liberty configuration generation failed. Full language server features + are unavailable until this is resolved. + + Error: [Brief error summary from Gradle output] + + Resolution steps: + 1. Ensure liberty-gradle-plugin version 3.11 or later + 2. Run 'gradle libertyPrepareConfig' manually to see full error + 3. Check build.gradle configuration + 4. Verify Gradle is installed and accessible + + [View detailed error log](.libertyls/prepare-config/build.log) + ``` +- User fixes issue in build.gradle or build.gradle.kts +- Regenerates successfully after fix + +--- + +## Scenario 5: Config File Change Detection + +### Maven Project +1. User modifies bootstrap.properties in `src/main/liberty/config/` +2. User adds new variable: `myapp.port=9080` +3. User saves bootstrap.properties +4. FileWatchService detects file modification +5. Listener calls SettingsService `populateVariablesForWorkspace()` method +6. SettingsService checks if config needs regeneration: + - Calls `ensureConfigIsUpToDate()` method + - Checks if pom.xml is newer than liberty-plugin-config.xml + - If stale, regenerates config automatically +7. Re-reads config from `target/liberty-plugin-config.xml` +8. Reads bootstrap.properties from `target/tmp/wlp/usr/servers/defaultServer/bootstrap.properties` +9. Parses configuration files for variable definitions +10. Extracts new variable `myapp.port=9080` +11. Updates variable values in workspace variable map +12. Next diagnostic run uses updated values: + - Variable references in server.xml validated against new values + - Completion suggestions include new variable + - Hover shows new variable value +13. No IDE restart required - changes take effect immediately + +### Gradle Project +Same flow as Maven, but: +- Reads config from `build/liberty-plugin-config.xml` +- Reads bootstrap.properties from `build/tmp/wlp/usr/servers/defaultServer/bootstrap.properties` +--- + +## Scenario 7: Source Config File Modified (Automatic Sync) + +This scenario demonstrates the automatic synchronization when a user modifies a config file in the source directory with the new file watching feature. + +### Maven Project +1. User modifies `src/main/liberty/config/bootstrap.properties` (source directory) +2. User adds new variable: `myapp.database.url=jdbc:mysql://localhost:3306/mydb` +3. User saves bootstrap.properties +4. **FileWatchService automatically detects the change** in `src/main/liberty/config/` directory +5. FileWatchService triggers prepare-config automatically: + - Executes `mvn liberty:prepare-config` in background + - Maven Plugin copies updated bootstrap.properties from source to mock server + - Resolves Maven properties in the updated file + - Generates new liberty-plugin-config.xml +6. After successful regeneration, FileWatchService immediately reloads variables: + - Calls SettingsService `populateVariablesForWorkspace()` method + - Reads updated bootstrap.properties from `target/tmp/wlp/usr/servers/defaultServer/` + - Extracts new variable `myapp.database.url=jdbc:mysql://localhost:3306/mydb` + - Updates variable map for workspace +7. Next diagnostic run (or current if in progress) uses updated values: + - Variable reference `${myapp.database.url}` now resolves correctly + - Code completion suggests new variable + - Hover shows new variable value +8. **No manual intervention required** - all automatic! + +### Gradle Project +Same flow as Maven, but: +- FileWatchService detects change in `src/main/liberty/config/` +- Executes `gradle libertyPrepareConfig` or `./gradlew libertyPrepareConfig` automatically +- Gradle Plugin copies updated file to `build/tmp/wlp/usr/servers/defaultServer/` +- Variables reloaded from updated mock server + +### Key Benefits + +**Seamless Developer Experience:** +- Changes to source config files automatically sync to mock server +- No need to manually run prepare-config +- No need to delete config files or directories +- No need to restart IDE or language server +- Variables update immediately after file save + +**How It Works:** +- FileWatchService monitors `src/main/liberty/config/` directory +- Detects ANY file change (create, modify) in that directory +- Automatically triggers prepare-config to sync changes +- Reloads variables after successful sync +- All happens in background without user intervention + + +--- + +## Scenario 6: Multi-Module Project + +### Maven Multi-Module Project +1. Project structure: + ``` + parent-project/ + ├── pom.xml (parent) + ├── module-a/ + │ ├── pom.xml + │ └── src/main/liberty/config/server.xml + └── module-b/ + ├── pom.xml + └── src/main/liberty/config/server.xml + ``` +2. User opens server.xml in module-a +3. LemMinX discovers workspace and identifies module-a as Liberty project +4. Check: liberty-plugin-config.xml doesn't exist in module-a/target +5. Execute: `mvn liberty:prepare-config` in module-a directory +6. Maven Plugin generates config for module-a: + - Creates `module-a/target/tmp/wlp/usr/servers/defaultServer/` + - Generates `module-a/target/liberty-plugin-config.xml` +7. Variables loaded for module-a workspace +8. User opens server.xml in module-b +9. LemMinX identifies module-b as separate Liberty project +10. Check: liberty-plugin-config.xml doesn't exist in module-b/target +11. Execute: `mvn liberty:prepare-config` in module-b directory +12. Maven Plugin generates config for module-b: + - Creates `module-b/target/tmp/wlp/usr/servers/defaultServer/` + - Generates `module-b/target/liberty-plugin-config.xml` +13. Variables loaded for module-b workspace +14. Each module has independent config and variables +15. Changes in one module don't affect the other + +### Gradle Multi-Module Project +Same flow as Maven, but: +- Project structure uses build.gradle files +- Execute: `gradle libertyPrepareConfig` in each module directory +- Creates `module-a/build/tmp/` and `module-b/build/tmp/` directories +- Generates separate config files in each module's build directory \ No newline at end of file diff --git a/docs/prepare-config.md b/docs/prepare-config.md index 733f5270b..5f24c9e9e 100644 --- a/docs/prepare-config.md +++ b/docs/prepare-config.md @@ -2,15 +2,21 @@ --- -Prepare Liberty configuration and generate `liberty-plugin-config.xml` without creating the server or installing Liberty. This lightweight goal evaluates project configuration and generates metadata needed by IDE tools and language servers. +Prepare Liberty configuration and generate `liberty-plugin-config.xml` with a mock Liberty server structure. This lightweight goal evaluates project configuration, creates a temporary Liberty server structure in `.libertyls` folder, copies all configuration files, and generates metadata needed by IDE tools and language servers. This goal is particularly useful for: - Enabling IDE support for Liberty configuration files (server.xml, bootstrap.properties, server.env) - Providing configuration metadata to language servers for code completion and diagnostics - Supporting Liberty Tools and other IDE extensions - Quick configuration validation without full project build +- Creating a mock Liberty server structure for language server integration -The goal does NOT install Liberty or create a server. It only generates the configuration metadata file based on your project's Maven configuration. +**What this goal does:** +1. Creates a mock Liberty server structure in `target/.libertyls/wlp/usr/servers/{serverName}/` +2. Copies all configuration files (server.xml, bootstrap.properties, server.env, jvm.options, etc.) to the mock server +3. Generates `liberty-plugin-config.xml` pointing to the mock server structure + +The goal does NOT install Liberty runtime. It only creates a minimal directory structure that mimics a Liberty server and copies configuration files. --- @@ -101,13 +107,31 @@ Configure the goal to run automatically during project initialization: --- -### Generated Configuration File +### Generated Configuration File and Mock Server Structure + +The `prepare-config` goal generates: + +1. **Mock Liberty Server Structure** in `target/.libertyls/`: + ``` + target/.libertyls/ + └── wlp/ + └── usr/ + └── servers/ + └── {serverName}/ + ├── server.xml + ├── bootstrap.properties + ├── server.env + ├── jvm.options + └── (other config files) + ``` -The `prepare-config` goal generates `target/liberty-plugin-config.xml` containing: +2. **Configuration Metadata File** `target/liberty-plugin-config.xml` containing: **Always included:** -- Install directory (if Liberty is already installed) -- Server name and directory paths +- Install directory (points to `target/.libertyls/wlp`) +- User directory (points to `target/.libertyls/wlp/usr`) +- Server directory (points to `target/.libertyls/wlp/usr/servers/{serverName}`) +- Server name and output directory paths - Project type (packaging) - Active build profiles - Project compile dependencies @@ -115,10 +139,10 @@ The `prepare-config` goal generates `target/liberty-plugin-config.xml` containin **Included when `includeServerInfo=true`:** - Configuration directory -- Server configuration file (`server.xml`) -- Bootstrap properties file (`bootstrap.properties`) -- JVM options file (`jvm.options`) -- Server environment file (`server.env`) +- Server configuration file path (in mock server) +- Bootstrap properties file path (in mock server) +- JVM options file path (in mock server) +- Server environment file path (in mock server) - Applications directory (`apps` or `dropins`) - Loose application configuration - Strip version settings @@ -179,12 +203,12 @@ mvn liberty:prepare-config ### Comparison with Other Goals -| Goal | Liberty Install | Server Creation | Config Files Copied | Use Case | -|------|----------------|-----------------|---------------------|----------| -| `prepare-config` | No | No | No | Generate config metadata for tools | -| `create` | Yes | Yes | Yes | Create and configure Liberty server | -| `install-server` | Yes | No | No | Install Liberty runtime only | -| `dev` | Yes | Yes | Yes | Development mode with hot reload | +| Goal | Liberty Install | Server Creation | Mock Server Structure | Config Files Copied | Use Case | +|------|----------------|-----------------|----------------------|---------------------|----------| +| `prepare-config` | No | No | Yes (in .libertyls) | Yes (to mock server) | Generate config metadata and mock structure for tools | +| `create` | Yes | Yes | No | Yes (to real server) | Create and configure Liberty server | +| `install-server` | Yes | No | No | No | Install Liberty runtime only | +| `dev` | Yes | Yes | No | Yes (to real server) | Development mode with hot reload | --- diff --git a/liberty-maven-plugin/src/it/prepare-config-it/src/test/java/net/wasdev/wlp/maven/test/app/PrepareConfigIT.java b/liberty-maven-plugin/src/it/prepare-config-it/src/test/java/net/wasdev/wlp/maven/test/app/PrepareConfigIT.java index 8f9d4f9d7..a37c8356a 100644 --- a/liberty-maven-plugin/src/it/prepare-config-it/src/test/java/net/wasdev/wlp/maven/test/app/PrepareConfigIT.java +++ b/liberty-maven-plugin/src/it/prepare-config-it/src/test/java/net/wasdev/wlp/maven/test/app/PrepareConfigIT.java @@ -124,15 +124,82 @@ public void testPluginConfigXmlContainsApplicationFilename() throws Exception { public void testNoTempDirectoryLeftBehind() throws Exception { // Check that no temporary Liberty installation directories are left behind File tempDir = new File(System.getProperty("java.io.tmpdir")); - File[] tempLibertyDirs = tempDir.listFiles((dir, name) -> + File[] tempLibertyDirs = tempDir.listFiles((dir, name) -> name.startsWith("liberty-temp-") && name.endsWith("-install")); if (tempLibertyDirs != null) { - Assert.assertEquals("No temporary Liberty directories should remain", + Assert.assertEquals("No temporary Liberty directories should remain", 0, tempLibertyDirs.length); } } + @Test + public void testMockLibertyServerStructureCreated() throws Exception { + // Verify that tmp directory structure was created + File tmpDir = new File("target/tmp"); + Assert.assertTrue("tmp directory should exist", tmpDir.exists()); + + File wlpDir = new File(tmpDir, "wlp"); + Assert.assertTrue("wlp directory should exist", wlpDir.exists()); + + File usrDir = new File(wlpDir, "usr"); + Assert.assertTrue("usr directory should exist", usrDir.exists()); + + File serversDir = new File(usrDir, "servers"); + Assert.assertTrue("servers directory should exist", serversDir.exists()); + + File serverDir = new File(serversDir, "testServer"); + Assert.assertTrue("testServer directory should exist", serverDir.exists()); + } + + @Test + public void testConfigFilesCopiedToMockServer() throws Exception { + // Verify that config files were copied to mock server structure + File mockServerDir = new File("target/tmp/wlp/usr/servers/testServer"); + + File serverXml = new File(mockServerDir, "server.xml"); + Assert.assertTrue("server.xml should be copied to mock server", serverXml.exists()); + + File bootstrapProps = new File(mockServerDir, "bootstrap.properties"); + Assert.assertTrue("bootstrap.properties should be copied to mock server", bootstrapProps.exists()); + } + + @Test + public void testPluginConfigXmlPointsToMockServer() throws Exception { + // Verify that liberty-plugin-config.xml has correct directory structure + // All directories should point to mock structure in tmp + + // installDirectory should point to mock Liberty in tmp + String installDir = getXPathValue("/liberty-plugin-config/installDirectory"); + Assert.assertNotNull("Install directory should be present", installDir); + Assert.assertTrue("Install directory should point to tmp/wlp", + installDir.contains("tmp") && installDir.endsWith("wlp")); + + // userDirectory should point to mock user directory in tmp + String userDir = getXPathValue("/liberty-plugin-config/userDirectory"); + Assert.assertNotNull("User directory should be present", userDir); + Assert.assertTrue("User directory should point to tmp/wlp/usr", + userDir.contains("tmp") && userDir.contains("wlp") && userDir.endsWith("usr")); + + // serverDirectory should point to mock server in tmp + String serverDir = getXPathValue("/liberty-plugin-config/serverDirectory"); + Assert.assertNotNull("Server directory should be present", serverDir); + Assert.assertTrue("Server directory should point to mock server in tmp", + serverDir.contains("tmp") && serverDir.contains("testServer")); + + // serverOutputDirectory should point to mock server in tmp + String serverOutputDir = getXPathValue("/liberty-plugin-config/serverOutputDirectory"); + Assert.assertNotNull("Server output directory should be present", serverOutputDir); + Assert.assertTrue("Server output directory should point to mock server in tmp", + serverOutputDir.contains("tmp") && serverOutputDir.contains("testServer")); + + // configFile should point to mock server + String configFile = getXPathValue("/liberty-plugin-config/configFile"); + Assert.assertNotNull("Config file should be present", configFile); + Assert.assertTrue("Config file should point to mock server in tmp", + configFile.contains("tmp") && configFile.contains("testServer")); + } + /** * Helper method to get XPath value from the plugin config XML */ diff --git a/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/PrepareConfigMojo.java b/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/PrepareConfigMojo.java index e78af78b0..d06fe4a31 100644 --- a/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/PrepareConfigMojo.java +++ b/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/PrepareConfigMojo.java @@ -106,10 +106,30 @@ private void doPrepareConfig() throws MojoExecutionException { getLog().info("Preparing Liberty configuration..."); try { - // Generate the liberty-plugin-config.xml file + // Create mock Liberty server structure in target/tmp folder + File mockServerDir = createMockLibertyServerStructure(); + + // Temporarily set serverDirectory to mock location for config file copying + File originalServerDir = serverDirectory; + try { + serverDirectory = mockServerDir; + + // Use parent's copyConfigFiles which handles: + // - configDirectory, serverXmlFile, jvmOptionsFile, bootstrapPropertiesFile, serverEnvFile overrides + // - inline configurations, mergeServerEnv logic, Maven property resolution + super.copyConfigFiles(); + + } finally { + // Restore original serverDirectory + serverDirectory = originalServerDir; + } + + // Generate liberty-plugin-config.xml with paths pointing to mock server File configFile = exportParametersToXml(includeServerInfo); getLog().info(MessageFormat.format("Liberty configuration file generated: {0}", configFile.getAbsolutePath())); + getLog().info(MessageFormat.format("Mock Liberty server structure created: {0}", + mockServerDir.getAbsolutePath())); } catch (IOException | ParserConfigurationException | TransformerException e) { throw new MojoExecutionException("Error preparing Liberty configuration.", e); @@ -117,17 +137,82 @@ private void doPrepareConfig() throws MojoExecutionException { } /** - * Override to prevent server creation during config preparation. - * This goal only generates the config file, it does not install Liberty or create a server. + * Create a mock Liberty server structure in target/tmp folder. + * This mimics the actual Liberty server directory structure without installing Liberty. + * + * Structure created: + * target/tmp/ + * └── wlp/ + * └── usr/ + * └── servers/ + * └── {serverName}/ + * ├── server.xml + * ├── bootstrap.properties + * ├── server.env + * └── jvm.options + * + * @return The mock server directory (target/tmp/wlp/usr/servers/{serverName}) + * @throws IOException if directory creation fails + */ + private File createMockLibertyServerStructure() throws IOException { + // Create tmp directory in target + File tmpDir = new File(project.getBuild().getDirectory(), "tmp"); + + // Create Liberty server structure: wlp/usr/servers/{serverName} + File wlpDir = new File(tmpDir, "wlp"); + File usrDir = new File(wlpDir, "usr"); + File serversDir = new File(usrDir, "servers"); + File mockServerDir = new File(serversDir, serverName); + + // Create all directories + if (!mockServerDir.exists()) { + if (!mockServerDir.mkdirs()) { + throw new IOException("Failed to create mock server directory: " + mockServerDir.getAbsolutePath()); + } + getLog().debug("Created mock server directory: " + mockServerDir.getAbsolutePath()); + } + + return mockServerDir; + } + /** + * Override to generate liberty-plugin-config.xml pointing to mock server structure. + * All directories (installDirectory, userDirectory, serverDirectory, serverOutputDirectory) + * are set to mock locations in target/tmp/wlp/usr/servers/{serverName}. */ @Override - protected void installServerAssembly() throws MojoExecutionException { - // Only export parameters, don't install Liberty or create server + protected File exportParametersToXml(boolean includeServerInfo) + throws IOException, ParserConfigurationException, TransformerException { + // Build mock Liberty directory structure paths + File tmpDir = new File(project.getBuild().getDirectory(), "tmp"); + File mockInstallDir = new File(tmpDir, "wlp"); + File mockUserDir = new File(mockInstallDir, "usr"); + File mockServersDir = new File(mockUserDir, "servers"); + File mockServerDir = new File(mockServersDir, serverName); + + // Save original values to restore after XML generation + File originalInstallDir = installDirectory; + File originalUserDir = userDirectory; + File originalServerDir = serverDirectory; + File originalOutputDir = outputDirectory; + try { - exportParametersToXml(false); - // Skip the actual Liberty installation - } catch (IOException | ParserConfigurationException | TransformerException e) { - throw new MojoExecutionException("Error exporting parameters.", e); + // Override all directories to point to mock structure + installDirectory = mockInstallDir; + userDirectory = mockUserDir; + serverDirectory = mockServerDir; + // Parent creates serverOutputDirectory as new File(outputDirectory, serverName) + // Set outputDirectory = mockServersDir to get mockServersDir/serverName = mockServerDir + outputDirectory = mockServersDir; + + // Call parent implementation with mock directories + return super.exportParametersToXml(includeServerInfo); + + } finally { + // Restore original values + installDirectory = originalInstallDir; + userDirectory = originalUserDir; + serverDirectory = originalServerDir; + outputDirectory = originalOutputDir; } } } \ No newline at end of file From 22b8025c4d8d0a9a67e95b57da4f9c683d635f7c Mon Sep 17 00:00:00 2001 From: Arun Venmany Date: Wed, 29 Apr 2026 09:27:36 +0530 Subject: [PATCH 3/9] prepare config updated design commit --- .../tools/maven/server/PrepareConfigMojo.java | 58 ++++--------------- 1 file changed, 11 insertions(+), 47 deletions(-) diff --git a/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/PrepareConfigMojo.java b/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/PrepareConfigMojo.java index d06fe4a31..41c5eb22a 100644 --- a/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/PrepareConfigMojo.java +++ b/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/PrepareConfigMojo.java @@ -27,6 +27,8 @@ import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; +import io.openliberty.tools.common.plugins.util.PrepareConfigUtil; + /** * Prepare Liberty configuration and generate liberty-plugin-config.xml without * creating the server. This lightweight goal evaluates project configuration @@ -106,8 +108,9 @@ private void doPrepareConfig() throws MojoExecutionException { getLog().info("Preparing Liberty configuration..."); try { - // Create mock Liberty server structure in target/tmp folder - File mockServerDir = createMockLibertyServerStructure(); + // Create mock Liberty server structure using common utility + File buildDirectory = new File(project.getBuild().getDirectory()); + File mockServerDir = PrepareConfigUtil.createMockLibertyServerStructure(buildDirectory, serverName); // Temporarily set serverDirectory to mock location for config file copying File originalServerDir = serverDirectory; @@ -135,45 +138,6 @@ private void doPrepareConfig() throws MojoExecutionException { throw new MojoExecutionException("Error preparing Liberty configuration.", e); } } - - /** - * Create a mock Liberty server structure in target/tmp folder. - * This mimics the actual Liberty server directory structure without installing Liberty. - * - * Structure created: - * target/tmp/ - * └── wlp/ - * └── usr/ - * └── servers/ - * └── {serverName}/ - * ├── server.xml - * ├── bootstrap.properties - * ├── server.env - * └── jvm.options - * - * @return The mock server directory (target/tmp/wlp/usr/servers/{serverName}) - * @throws IOException if directory creation fails - */ - private File createMockLibertyServerStructure() throws IOException { - // Create tmp directory in target - File tmpDir = new File(project.getBuild().getDirectory(), "tmp"); - - // Create Liberty server structure: wlp/usr/servers/{serverName} - File wlpDir = new File(tmpDir, "wlp"); - File usrDir = new File(wlpDir, "usr"); - File serversDir = new File(usrDir, "servers"); - File mockServerDir = new File(serversDir, serverName); - - // Create all directories - if (!mockServerDir.exists()) { - if (!mockServerDir.mkdirs()) { - throw new IOException("Failed to create mock server directory: " + mockServerDir.getAbsolutePath()); - } - getLog().debug("Created mock server directory: " + mockServerDir.getAbsolutePath()); - } - - return mockServerDir; - } /** * Override to generate liberty-plugin-config.xml pointing to mock server structure. * All directories (installDirectory, userDirectory, serverDirectory, serverOutputDirectory) @@ -182,12 +146,12 @@ private File createMockLibertyServerStructure() throws IOException { @Override protected File exportParametersToXml(boolean includeServerInfo) throws IOException, ParserConfigurationException, TransformerException { - // Build mock Liberty directory structure paths - File tmpDir = new File(project.getBuild().getDirectory(), "tmp"); - File mockInstallDir = new File(tmpDir, "wlp"); - File mockUserDir = new File(mockInstallDir, "usr"); - File mockServersDir = new File(mockUserDir, "servers"); - File mockServerDir = new File(mockServersDir, serverName); + // Build mock Liberty directory structure paths using common utility + File buildDirectory = new File(project.getBuild().getDirectory()); + File mockInstallDir = PrepareConfigUtil.getMockInstallDirectory(buildDirectory); + File mockUserDir = PrepareConfigUtil.getMockUserDirectory(buildDirectory); + File mockServersDir = PrepareConfigUtil.getMockServersDirectory(buildDirectory); + File mockServerDir = PrepareConfigUtil.getMockServerDirectory(buildDirectory, serverName); // Save original values to restore after XML generation File originalInstallDir = installDirectory; From 6000ed707db3f604255b3e1810d7489fc8d70de9 Mon Sep 17 00:00:00 2001 From: Arun Venmany Date: Mon, 4 May 2026 10:20:21 +0530 Subject: [PATCH 4/9] prepare config adding integration tests --- .github/workflows/maven.yml | 5 +- liberty-maven-plugin/pom.xml | 2 +- .../wlp/maven/test/app/PrepareConfigIT.java | 166 +++++++++-- .../invoker.properties | 3 + .../prepare-config-no-server-info-it/pom.xml | 58 ++++ .../src/main/liberty/config/server.xml | 14 + .../test/app/PrepareConfigNoServerInfoIT.java | 274 ++++++++++++++++++ 7 files changed, 502 insertions(+), 20 deletions(-) create mode 100644 liberty-maven-plugin/src/it/prepare-config-no-server-info-it/invoker.properties create mode 100644 liberty-maven-plugin/src/it/prepare-config-no-server-info-it/pom.xml create mode 100644 liberty-maven-plugin/src/it/prepare-config-no-server-info-it/src/main/liberty/config/server.xml create mode 100644 liberty-maven-plugin/src/it/prepare-config-no-server-info-it/src/test/java/net/wasdev/wlp/maven/test/app/PrepareConfigNoServerInfoIT.java diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 73ef567c7..e95bc5fbf 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -98,8 +98,9 @@ jobs: - name: Checkout ci.common uses: actions/checkout@v3 with: - repository: OpenLiberty/ci.common + repository: venmanyarun/ci.common path: ci.common + ref: prepare_config_poc - name: Checkout ci.ant uses: actions/checkout@v3 with: @@ -206,7 +207,7 @@ jobs: - name: Clone ci.ant and ci.common repos to github.workspace run: | echo ${{github.workspace}} - git clone https://github.com/OpenLiberty/ci.common.git ${{github.workspace}}/ci.common + git clone https://github.com/venmanyarun/ci.common.git --branch prepare_config_poc --single-branch ${{github.workspace}}/ci.common git clone https://github.com/OpenLiberty/ci.ant.git ${{github.workspace}}/ci.ant - name: Set up Maven uses: stCarolas/setup-maven@v4.5 diff --git a/liberty-maven-plugin/pom.xml b/liberty-maven-plugin/pom.xml index d212efabf..7f339fed7 100644 --- a/liberty-maven-plugin/pom.xml +++ b/liberty-maven-plugin/pom.xml @@ -89,7 +89,7 @@ io.openliberty.tools ci.common - 1.8.41 + 1.8.42-SNAPSHOT org.apache.commons diff --git a/liberty-maven-plugin/src/it/prepare-config-it/src/test/java/net/wasdev/wlp/maven/test/app/PrepareConfigIT.java b/liberty-maven-plugin/src/it/prepare-config-it/src/test/java/net/wasdev/wlp/maven/test/app/PrepareConfigIT.java index a37c8356a..e30b8573d 100644 --- a/liberty-maven-plugin/src/it/prepare-config-it/src/test/java/net/wasdev/wlp/maven/test/app/PrepareConfigIT.java +++ b/liberty-maven-plugin/src/it/prepare-config-it/src/test/java/net/wasdev/wlp/maven/test/app/PrepareConfigIT.java @@ -17,16 +17,23 @@ import java.io.File; import java.io.FileInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import org.junit.Assert; import org.junit.Test; import org.w3c.dom.Document; import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; /** * Integration test for prepare-config goal @@ -34,6 +41,10 @@ public class PrepareConfigIT { private static final String PLUGIN_CONFIG_XML = "target/liberty-plugin-config.xml"; + public static final String TARGET_TMP_WLP_USR_SERVERS_TEST_SERVER = "target/tmp/wlp/usr/servers/testServer"; + public static final String BOOTSTRAP_PROPERTIES = "bootstrap.properties"; + public static final String SERVER_XML = "server.xml"; + public static final String LIBERTY_PLUGIN_CONFIG_CONFIG_FILE = "/liberty-plugin-config/configFile"; @Test public void testPluginConfigXmlExists() throws Exception { @@ -55,9 +66,9 @@ public void testPluginConfigXmlContainsProjectType() throws Exception { @Test public void testPluginConfigXmlContainsConfigFile() throws Exception { - String configFile = getXPathValue("/liberty-plugin-config/configFile"); + String configFile = getXPathValue(LIBERTY_PLUGIN_CONFIG_CONFIG_FILE); Assert.assertNotNull("Config file should be present", configFile); - Assert.assertTrue("Config file should reference server.xml", configFile.contains("server.xml")); + Assert.assertTrue("Config file should reference server.xml", configFile.contains(SERVER_XML)); } @Test @@ -65,7 +76,7 @@ public void testPluginConfigXmlContainsBootstrapPropertiesFile() throws Exceptio String bootstrapFile = getXPathValue("/liberty-plugin-config/bootstrapPropertiesFile"); Assert.assertNotNull("Bootstrap properties file should be present", bootstrapFile); Assert.assertTrue("Bootstrap file should reference bootstrap.properties", - bootstrapFile.contains("bootstrap.properties")); + bootstrapFile.contains(BOOTSTRAP_PROPERTIES)); } @Test @@ -155,12 +166,12 @@ public void testMockLibertyServerStructureCreated() throws Exception { @Test public void testConfigFilesCopiedToMockServer() throws Exception { // Verify that config files were copied to mock server structure - File mockServerDir = new File("target/tmp/wlp/usr/servers/testServer"); + File mockServerDir = new File(TARGET_TMP_WLP_USR_SERVERS_TEST_SERVER); - File serverXml = new File(mockServerDir, "server.xml"); + File serverXml = new File(mockServerDir, SERVER_XML); Assert.assertTrue("server.xml should be copied to mock server", serverXml.exists()); - File bootstrapProps = new File(mockServerDir, "bootstrap.properties"); + File bootstrapProps = new File(mockServerDir, BOOTSTRAP_PROPERTIES); Assert.assertTrue("bootstrap.properties should be copied to mock server", bootstrapProps.exists()); } @@ -194,7 +205,7 @@ public void testPluginConfigXmlPointsToMockServer() throws Exception { serverOutputDir.contains("tmp") && serverOutputDir.contains("testServer")); // configFile should point to mock server - String configFile = getXPathValue("/liberty-plugin-config/configFile"); + String configFile = getXPathValue(LIBERTY_PLUGIN_CONFIG_CONFIG_FILE); Assert.assertNotNull("Config file should be present", configFile); Assert.assertTrue("Config file should point to mock server in tmp", configFile.contains("tmp") && configFile.contains("testServer")); @@ -203,11 +214,8 @@ public void testPluginConfigXmlPointsToMockServer() throws Exception { /** * Helper method to get XPath value from the plugin config XML */ - private String getXPathValue(String expression) throws Exception { - File configFile = new File(PLUGIN_CONFIG_XML); - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - DocumentBuilder builder = factory.newDocumentBuilder(); - Document doc = builder.parse(new FileInputStream(configFile)); + private String getXPathValue(String expression) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { + Document doc = getDocument(configFile); XPathFactory xPathfactory = XPathFactory.newInstance(); XPath xpath = xPathfactory.newXPath(); @@ -218,15 +226,139 @@ private String getXPathValue(String expression) throws Exception { /** * Helper method to get XPath NodeList from the plugin config XML */ - private NodeList getXPathNodeList(String expression) throws Exception { + private NodeList getXPathNodeList(String expression) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { File configFile = new File(PLUGIN_CONFIG_XML); - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - DocumentBuilder builder = factory.newDocumentBuilder(); - Document doc = builder.parse(new FileInputStream(configFile)); - + Document doc = getDocument(configFile); + XPathFactory xPathfactory = XPathFactory.newInstance(); XPath xpath = xPathfactory.newXPath(); return (NodeList) xpath.evaluate(expression, doc, XPathConstants.NODESET); } + + private static Document getDocument(File configFile) { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + + // Protect against XXE attacks + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + factory.setFeature("http://xml.org/sax/features/external-general-entities", false); + factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + factory.setXIncludeAware(false); + factory.setExpandEntityReferences(false); + + factory.setIgnoringComments(true); + factory.setCoalescing(true); + factory.setIgnoringElementContentWhitespace(true); + factory.setValidating(false); + + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc; + try (FileInputStream fis = new FileInputStream(configFile)) { + doc = builder.parse(fis); + } + return doc; + } + + @Test + public void testPluginConfigXmlContainsUserDirectory() throws Exception { + String userDirectory = getXPathValue("/liberty-plugin-config/userDirectory"); + Assert.assertNotNull("User directory should be present", userDirectory); + Assert.assertTrue("User directory should point to mock user directory", + userDirectory.contains("tmp") && userDirectory.contains("usr")); + } + + @Test + public void testPluginConfigXmlContainsServerOutputDirectory() throws Exception { + String serverOutputDirectory = getXPathValue("/liberty-plugin-config/serverOutputDirectory"); + Assert.assertNotNull("Server output directory should be present", serverOutputDirectory); + Assert.assertTrue("Server output directory should point to mock server", + serverOutputDirectory.contains("tmp") && serverOutputDirectory.contains("testServer")); + } + + @Test + public void testBootstrapPropertiesContentCopied() throws Exception { + // Verify that bootstrap.properties was copied with correct content + File mockServerDir = new File(TARGET_TMP_WLP_USR_SERVERS_TEST_SERVER); + File bootstrapProps = new File(mockServerDir, BOOTSTRAP_PROPERTIES); + + Assert.assertTrue("bootstrap.properties should exist", bootstrapProps.exists()); + + // Read and verify content + String content = new String(Files.readAllBytes(bootstrapProps.toPath()), StandardCharsets.UTF_8); + + Assert.assertTrue("Bootstrap properties should contain default.http.port", + content.contains("default.http.port")); + Assert.assertTrue("Bootstrap properties should contain default.https.port", + content.contains("default.https.port")); + } + + @Test + public void testServerXmlContentCopied() throws Exception { + // Verify that server.xml was copied with correct content + File mockServerDir = new File(TARGET_TMP_WLP_USR_SERVERS_TEST_SERVER); + File serverXml = new File(mockServerDir, SERVER_XML); + + Assert.assertTrue("server.xml should exist", serverXml.exists()); + + // Read and verify content + String content = new String(Files.readAllBytes(serverXml.toPath()), StandardCharsets.UTF_8); + + Assert.assertTrue("server.xml should contain featureManager", + content.contains("featureManager")); + Assert.assertTrue("server.xml should contain jakartaee-9.1 feature", + content.contains("jakartaee-9.1")); + Assert.assertTrue("server.xml should contain microProfile-5.0 feature", + content.contains("microProfile-5.0")); + } + + @Test + public void testPluginConfigXmlContainsProjectDirectory() throws Exception { + String projectDirectory = getXPathValue("/liberty-plugin-config/projectDirectory"); + Assert.assertNotNull("Project directory should be present", projectDirectory); + Assert.assertFalse("Project directory should not be empty", projectDirectory.trim().isEmpty()); + } + + @Test + public void testPluginConfigXmlContainsBuildDirectory() throws Exception { + String buildDirectory = getXPathValue("/liberty-plugin-config/buildDirectory"); + Assert.assertNotNull("Build directory should be present", buildDirectory); + Assert.assertTrue("Build directory should contain 'target'", buildDirectory.contains("target")); + } + + @Test + public void testMockServerStructureIsComplete() throws Exception { + // Comprehensive check of the entire mock server structure + File tmpDir = new File("target/tmp"); + File wlpDir = new File(tmpDir, "wlp"); + File usrDir = new File(wlpDir, "usr"); + File serversDir = new File(usrDir, "servers"); + File serverDir = new File(serversDir, "testServer"); + + Assert.assertTrue("tmp directory should be a directory", tmpDir.isDirectory()); + Assert.assertTrue("wlp directory should be a directory", wlpDir.isDirectory()); + Assert.assertTrue("usr directory should be a directory", usrDir.isDirectory()); + Assert.assertTrue("servers directory should be a directory", serversDir.isDirectory()); + Assert.assertTrue("testServer directory should be a directory", serverDir.isDirectory()); + + // Verify the structure matches Liberty's expected layout + Assert.assertEquals("wlp should be child of tmp", tmpDir, wlpDir.getParentFile()); + Assert.assertEquals("usr should be child of wlp", wlpDir, usrDir.getParentFile()); + Assert.assertEquals("servers should be child of usr", usrDir, serversDir.getParentFile()); + Assert.assertEquals("testServer should be child of servers", serversDir, serverDir.getParentFile()); + } + + @Test + public void testIncludeServerInfoIsTrue() throws Exception { + // Verify that when includeServerInfo=true (default), server files are included + String configFile = getXPathValue(LIBERTY_PLUGIN_CONFIG_CONFIG_FILE); + String bootstrapFile = getXPathValue("/liberty-plugin-config/bootstrapPropertiesFile"); + + Assert.assertNotNull("Config file should be present when includeServerInfo=true", configFile); + Assert.assertFalse("Config file should not be empty", configFile.trim().isEmpty()); + + Assert.assertNotNull("Bootstrap file should be present when includeServerInfo=true", bootstrapFile); + Assert.assertFalse("Bootstrap file should not be empty", bootstrapFile.trim().isEmpty()); + } } \ No newline at end of file diff --git a/liberty-maven-plugin/src/it/prepare-config-no-server-info-it/invoker.properties b/liberty-maven-plugin/src/it/prepare-config-no-server-info-it/invoker.properties new file mode 100644 index 000000000..dcb4f135f --- /dev/null +++ b/liberty-maven-plugin/src/it/prepare-config-no-server-info-it/invoker.properties @@ -0,0 +1,3 @@ +# Integration test configuration for prepare-config goal with includeServerInfo=false +invoker.goals=initialize +invoker.maven.version=3.6.0+ \ No newline at end of file diff --git a/liberty-maven-plugin/src/it/prepare-config-no-server-info-it/pom.xml b/liberty-maven-plugin/src/it/prepare-config-no-server-info-it/pom.xml new file mode 100644 index 000000000..b6aceafa0 --- /dev/null +++ b/liberty-maven-plugin/src/it/prepare-config-no-server-info-it/pom.xml @@ -0,0 +1,58 @@ + + + 4.0.0 + + net.wasdev.wlp.maven.test + prepare-config-no-server-info-it + 1.0-SNAPSHOT + war + + + UTF-8 + 1.8 + 1.8 + + + + + jakarta.platform + jakarta.jakartaee-web-api + 9.1.0 + provided + + + + + ${project.artifactId} + + + @project.groupId@ + @project.artifactId@ + @project.version@ + + + prepare-config + initialize + + prepare-config + + + testServer + false + + + + + + ${runtimeGroupId} + ${runtimeArtifactId} + ${runtimeVersion} + zip + + + + + + \ No newline at end of file diff --git a/liberty-maven-plugin/src/it/prepare-config-no-server-info-it/src/main/liberty/config/server.xml b/liberty-maven-plugin/src/it/prepare-config-no-server-info-it/src/main/liberty/config/server.xml new file mode 100644 index 000000000..a1eddc8af --- /dev/null +++ b/liberty-maven-plugin/src/it/prepare-config-no-server-info-it/src/main/liberty/config/server.xml @@ -0,0 +1,14 @@ + + + + + jakartaee-9.1 + + + + + + + diff --git a/liberty-maven-plugin/src/it/prepare-config-no-server-info-it/src/test/java/net/wasdev/wlp/maven/test/app/PrepareConfigNoServerInfoIT.java b/liberty-maven-plugin/src/it/prepare-config-no-server-info-it/src/test/java/net/wasdev/wlp/maven/test/app/PrepareConfigNoServerInfoIT.java new file mode 100644 index 000000000..4bdf42dfb --- /dev/null +++ b/liberty-maven-plugin/src/it/prepare-config-no-server-info-it/src/test/java/net/wasdev/wlp/maven/test/app/PrepareConfigNoServerInfoIT.java @@ -0,0 +1,274 @@ +/** + * (C) Copyright IBM Corporation 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 the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.wasdev.wlp.maven.test.app; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +import org.junit.Assert; +import org.junit.Test; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +/** + * Integration test for prepare-config goal with includeServerInfo=false + * This tests the lightweight mode that only includes basic project information + * without server-specific configuration file paths. + */ +public class PrepareConfigNoServerInfoIT { + + private static final String PLUGIN_CONFIG_XML = "target/liberty-plugin-config.xml"; + + @Test + public void testPluginConfigXmlExists() throws Exception { + File configFile = new File(PLUGIN_CONFIG_XML); + Assert.assertTrue("liberty-plugin-config.xml should exist", configFile.exists()); + } + + @Test + public void testPluginConfigXmlContainsServerName() throws Exception { + String serverName = getXPathValue("/liberty-plugin-config/serverName"); + Assert.assertEquals("Server name should be testServer", "testServer", serverName); + } + + @Test + public void testPluginConfigXmlContainsProjectType() throws Exception { + String projectType = getXPathValue("/liberty-plugin-config/projectType"); + Assert.assertEquals("Project type should be war", "war", projectType); + } + + @Test + public void testPluginConfigXmlDoesNotContainConfigFile() throws Exception { + String configFile = getXPathValue("/liberty-plugin-config/configFile"); + Assert.assertTrue("Config file should be empty or null when includeServerInfo=false", + configFile == null || configFile.trim().isEmpty()); + } + + @Test + public void testPluginConfigXmlDoesNotContainBootstrapPropertiesFile() throws Exception { + String bootstrapFile = getXPathValue("/liberty-plugin-config/bootstrapPropertiesFile"); + Assert.assertTrue("Bootstrap properties file should be empty or null when includeServerInfo=false", + bootstrapFile == null || bootstrapFile.trim().isEmpty()); + } + + @Test + public void testPluginConfigXmlDoesNotContainServerEnvFile() throws Exception { + String serverEnvFile = getXPathValue("/liberty-plugin-config/serverEnvFile"); + Assert.assertTrue("Server env file should be empty or null when includeServerInfo=false", + serverEnvFile == null || serverEnvFile.trim().isEmpty()); + } + + @Test + public void testPluginConfigXmlDoesNotContainJvmOptionsFile() throws Exception { + String jvmOptionsFile = getXPathValue("/liberty-plugin-config/jvmOptionsFile"); + Assert.assertTrue("JVM options file should be empty or null when includeServerInfo=false", + jvmOptionsFile == null || jvmOptionsFile.trim().isEmpty()); + } + + @Test + public void testPluginConfigXmlContainsServerDirectory() throws Exception { + String serverDirectory = getXPathValue("/liberty-plugin-config/serverDirectory"); + Assert.assertNotNull("Server directory should still be present", serverDirectory); + } + + @Test + public void testPluginConfigXmlContainsInstallDirectory() throws Exception { + String installDirectory = getXPathValue("/liberty-plugin-config/installDirectory"); + Assert.assertNotNull("Install directory should still be present", installDirectory); + } + + @Test + public void testPluginConfigXmlContainsLooseApplication() throws Exception { + String looseApp = getXPathValue("/liberty-plugin-config/looseApplication"); + Assert.assertNotNull("Loose application setting should be present", looseApp); + } + + @Test + public void testPluginConfigXmlContainsDependencies() throws Exception { + NodeList dependencies = getXPathNodeList("/liberty-plugin-config/projectCompileDependency"); + Assert.assertNotNull("Dependencies should be present", dependencies); + Assert.assertTrue("Should have at least one dependency", dependencies.getLength() > 0); + + // Check for Jakarta EE dependency + boolean foundJakartaEE = false; + for (int i = 0; i < dependencies.getLength(); i++) { + String dep = dependencies.item(i).getTextContent(); + if (dep.contains("jakarta.jakartaee-web-api")) { + foundJakartaEE = true; + break; + } + } + Assert.assertTrue("Should contain Jakarta EE dependency", foundJakartaEE); + } + + @Test + public void testPluginConfigXmlContainsApplicationFilename() throws Exception { + String appFilename = getXPathValue("/liberty-plugin-config/applicationFilename"); + Assert.assertNotNull("Application filename should be present", appFilename); + Assert.assertTrue("Application filename should be prepare-config-no-server-info-it.war.xml (loose app)", + appFilename.equals("prepare-config-no-server-info-it.war.xml")); + } + + @Test + public void testMockLibertyServerStructureCreated() throws Exception { + // Verify that tmp directory structure was created even with includeServerInfo=false + File tmpDir = new File("target/tmp"); + Assert.assertTrue("tmp directory should exist", tmpDir.exists()); + + File wlpDir = new File(tmpDir, "wlp"); + Assert.assertTrue("wlp directory should exist", wlpDir.exists()); + + File usrDir = new File(wlpDir, "usr"); + Assert.assertTrue("usr directory should exist", usrDir.exists()); + + File serversDir = new File(usrDir, "servers"); + Assert.assertTrue("servers directory should exist", serversDir.exists()); + + File serverDir = new File(serversDir, "testServer"); + Assert.assertTrue("testServer directory should exist", serverDir.exists()); + } + + @Test + public void testConfigFilesNotCopiedToMockServer() throws Exception { + // When includeServerInfo=false, config files should still be copied + // because copyConfigFiles is called before the includeServerInfo check + File mockServerDir = new File("target/tmp/wlp/usr/servers/testServer"); + + File serverXml = new File(mockServerDir, "server.xml"); + Assert.assertTrue("server.xml should be copied to mock server", serverXml.exists()); + } + + @Test + public void testPluginConfigXmlPointsToMockServer() throws Exception { + // Verify that liberty-plugin-config.xml has correct directory structure + // All directories should point to mock structure in tmp + + // installDirectory should point to mock Liberty in tmp + String installDir = getXPathValue("/liberty-plugin-config/installDirectory"); + Assert.assertNotNull("Install directory should be present", installDir); + Assert.assertTrue("Install directory should point to tmp/wlp", + installDir.contains("tmp") && installDir.endsWith("wlp")); + + // userDirectory should point to mock user directory in tmp + String userDir = getXPathValue("/liberty-plugin-config/userDirectory"); + Assert.assertNotNull("User directory should be present", userDir); + Assert.assertTrue("User directory should point to tmp/wlp/usr", + userDir.contains("tmp") && userDir.contains("wlp") && userDir.endsWith("usr")); + + // serverDirectory should point to mock server in tmp + String serverDir = getXPathValue("/liberty-plugin-config/serverDirectory"); + Assert.assertNotNull("Server directory should be present", serverDir); + Assert.assertTrue("Server directory should point to mock server in tmp", + serverDir.contains("tmp") && serverDir.contains("testServer")); + + // serverOutputDirectory should point to mock server in tmp + String serverOutputDir = getXPathValue("/liberty-plugin-config/serverOutputDirectory"); + Assert.assertNotNull("Server output directory should be present", serverOutputDir); + Assert.assertTrue("Server output directory should point to mock server in tmp", + serverOutputDir.contains("tmp") && serverOutputDir.contains("testServer")); + } + + @Test + public void testFasterExecutionWithoutServerInfo() throws Exception { + // This test verifies that the goal completes successfully + // The actual performance benefit is that server-specific file paths + // are not included in the XML, making it lighter weight + File configFile = new File(PLUGIN_CONFIG_XML); + Assert.assertTrue("Config file should exist", configFile.exists()); + + // Verify the file is smaller/simpler by checking it doesn't contain + // server-specific file paths + String configFile1 = getXPathValue("/liberty-plugin-config/configFile"); + String bootstrapFile = getXPathValue("/liberty-plugin-config/bootstrapPropertiesFile"); + String serverEnvFile = getXPathValue("/liberty-plugin-config/serverEnvFile"); + String jvmOptionsFile = getXPathValue("/liberty-plugin-config/jvmOptionsFile"); + + boolean hasNoServerFiles = (configFile1 == null || configFile1.trim().isEmpty()) && + (bootstrapFile == null || bootstrapFile.trim().isEmpty()) && + (serverEnvFile == null || serverEnvFile.trim().isEmpty()) && + (jvmOptionsFile == null || jvmOptionsFile.trim().isEmpty()); + + Assert.assertTrue("Config should not contain server-specific file paths", hasNoServerFiles); + } + + /** + * Helper method to get XPath value from the plugin config XML + */ + private String getXPathValue(String expression) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { + File configFile = new File(PLUGIN_CONFIG_XML); + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + + // Protect against XXE attacks + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + factory.setFeature("http://xml.org/sax/features/external-general-entities", false); + factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + factory.setXIncludeAware(false); + factory.setExpandEntityReferences(false); + + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc; + try (FileInputStream fis = new FileInputStream(configFile)) { + doc = builder.parse(fis); + } + + XPathFactory xPathfactory = XPathFactory.newInstance(); + XPath xpath = xPathfactory.newXPath(); + + return xpath.evaluate(expression, doc); + } + + /** + * Helper method to get XPath NodeList from the plugin config XML + */ + private NodeList getXPathNodeList(String expression) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { + File configFile = new File(PLUGIN_CONFIG_XML); + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + + // Protect against XXE attacks + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + factory.setFeature("http://xml.org/sax/features/external-general-entities", false); + factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + factory.setXIncludeAware(false); + factory.setExpandEntityReferences(false); + + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc; + try (FileInputStream fis = new FileInputStream(configFile)) { + doc = builder.parse(fis); + } + + XPathFactory xPathfactory = XPathFactory.newInstance(); + XPath xpath = xPathfactory.newXPath(); + + return (NodeList) xpath.evaluate(expression, doc, XPathConstants.NODESET); + } +} From bb16076125abde5612dcfa8a8f7fc0c30124b5d8 Mon Sep 17 00:00:00 2001 From: Arun Venmany Date: Mon, 4 May 2026 10:38:27 +0530 Subject: [PATCH 5/9] prepare config removing documentation --- docs/prepare-config-complete-flow.md | 255 --------------------- docs/prepare-config-scenarios.md | 326 --------------------------- 2 files changed, 581 deletions(-) delete mode 100644 docs/prepare-config-complete-flow.md delete mode 100644 docs/prepare-config-scenarios.md diff --git a/docs/prepare-config-complete-flow.md b/docs/prepare-config-complete-flow.md deleted file mode 100644 index bbd5a1ac7..000000000 --- a/docs/prepare-config-complete-flow.md +++ /dev/null @@ -1,255 +0,0 @@ -# Liberty Config Language Server Integration - Complete Flow - -## Overview -This document describes the integration between Liberty Maven/Gradle plugins and the Liberty Config Language Server (LCLS), focusing on the prepare-config goal/task and on-demand configuration generation. - -## Metadata Used by LCLS from liberty-plugin-config.xml - -### lemminx-liberty (Language Server) -- **installDirectory** - Load Liberty runtime metadata for variable resolution -- **serverDirectory** - Locate server.xml and related config files -- **userDirectory** - Access shared configuration files -- **serverOutputDirectory** - Resolve output directory variables -- **configFile** - Custom server.xml location, used for non-standard paths - -### liberty-ls (Legacy Language Server) -- **serverEnv** - Custom server.env file path -- **bootstrapPropertiesFile** - Custom bootstrap.properties file path -- **configDirectory** - Configuration directory location - ---- - -## Part 1: Liberty Plugin Flow (Maven/Gradle) - -### Command Execution -```bash -# Maven -mvn liberty:prepare-config - -# Gradle -gradle libertyPrepareConfig -``` - -### Step 1: Plugin Invocation -User or IDE executes prepare-config command. Plugin framework initializes the goal/task and reads configuration from pom.xml (Maven) or build.gradle (Gradle). - -### Step 2: Goal/Task Initialization -Reads build configuration and initializes plugin parameters: -- `includeServerInfo = true` (default) - Controls whether to include server-specific information -- `serverName` from configuration (default: "defaultServer") -- `installDirectory`, `configDirectory`, `userDirectory`, `outputDirectory` from configuration -- Validates skip parameter, exits early if skip is true - -### Step 3: Create Mock Liberty Server Structure -Creates directory structure in build output folder: -- **Maven**: Creates `target/tmp/wlp/usr/servers/{serverName}/` directory structure -- **Gradle**: Creates `build/tmp/wlp/usr/servers/{serverName}/` directory structure -- Mimics actual Liberty server layout without installing Liberty -- All directories created with proper permissions -- Returns the mock server directory path for subsequent operations - -### Step 4: Copy Configuration Files to Mock Server -Uses parent class `copyConfigFiles()` method which handles: -- Copies all files from configured `configDirectory` if specified -- Uses explicit file overrides (serverXmlFile, jvmOptionsFile, bootstrapPropertiesFile, serverEnvFile) -- Applies inline bootstrap properties and JVM options if configured in build file -- Executes `mergeServerEnv` logic if configured -- Resolves build property references (Maven: `${property}`, Gradle: `${property}`) in all config files -- Processes `` elements in server.xml -- Temporarily sets `serverDirectory` to mock location before copying, then restores original value - -### Step 5: Read Project Information -Extract project metadata from build configuration: -- Reads project coordinates: `groupId`, `artifactId`, `version`, `packaging` type -- Collects all compile-scope and runtime-scope dependencies with their coordinates -- Identifies active build profiles (Maven) or configurations (Gradle) - -### Step 6: Generate Configuration XML - -Builds mock directory paths and saves original directory values for restoration. Overrides directories to point to mock structure: -- **Maven**: Sets `installDirectory` to `target/tmp/wlp` -- **Gradle**: Sets `installDirectory` to `build/tmp/wlp` -- Sets `userDirectory` to `{buildDir}/tmp/wlp/usr` -- Sets `serverDirectory` to `{buildDir}/tmp/wlp/usr/servers/{serverName}` -- Sets `outputDirectory` to `{buildDir}/tmp/wlp/usr/servers` - -Creates XML document with root element ``: - -**Always-Included Elements:** -- ``, ``, ``, `` - All point to mock structure -- ``, ``, ``, `` - Project metadata - -**Server-Specific Information (if includeServerInfo=true):** -- ``, ``, ``, ``, `` -- ``, ``, `` - -Calls parent implementation with mock directories, then restores original directory values. - -### Step 7: Write Configuration File -Writes XML content to build output directory: -- **Maven**: `target/liberty-plugin-config.xml` -- **Gradle**: `build/liberty-plugin-config.xml` - -Closes file handles and verifies file was written successfully. - -### Step 8: Completion -Logs success messages and returns success status to build tool. - ---- - -## Part 2: Liberty Config Language Server (LCLS) Flow - -### A. Extension Startup - -#### Step 1: Initialize Extension -LemMinX (XML Language Server) starts and loads Liberty extension. Registers completion, hover, diagnostics, code actions, and document link participants. - -#### Step 2: Discover Workspaces -LibertyProjectsManager receives workspace folders from IDE, recursively searches for XML files with `` root element, detects multi-module Maven and Gradle projects, and creates LibertyWorkspace instances. - -#### Step 3: Initialize Variables Map -Calls SettingsService to initialize empty variables map. Map is created but not populated with values yet. Actual variable population happens on-demand when needed. This ensures map is never null when accessed later. - -#### Step 4: Start File Monitoring -FileWatchService monitors workspace directories for changes to server.xml, bootstrap.properties, server.env, jvm.options, and files referenced via `` elements. When changes detected, triggers variable reload for affected workspace. - -### B. File Open Detection (Diagnostics Trigger) - -#### Step 1: File Open Event -User opens server.xml or any Liberty config file in IDE. LemMinX triggers LibertyDiagnosticParticipant `doDiagnostics()` method. - -#### Step 2: Trigger Automatic Config Generation -Gets workspace folder for the opened document. Checks if project has Liberty Maven or Gradle plugin configured in build file. Calls `needsConfigGeneration()` to determine if regeneration is needed. If needed, executes prepare-config command with 5-second timeout. - -#### Step 3: Check for Configuration File (needsConfigGeneration) -Performs four checks to determine if config generation is needed: - -**Check 1: Config file doesn't exist** -- **Maven**: Looks for liberty-plugin-config.xml in target directory -- **Gradle**: Looks for liberty-plugin-config.xml in build directory -- If not found, removes project from processed cache and returns true - -**Check 2: Config points to mock server but mock directory missing** -- Reads config file and verifies mock directory exists -- **Maven**: Checks for `target/tmp/wlp/usr/servers/{serverName}` directory -- **Gradle**: Checks for `build/tmp/wlp/usr/servers/{serverName}` directory -- If missing (e.g., after clean), returns true - -**Check 3: Already processed in this session** -- Checks if project path exists in processedProjects set -- If found, returns false to skip generation - -**Check 4: Config is stale (older than build file)** -- Compares timestamps of liberty-plugin-config.xml and build file -- **Maven**: Compares with pom.xml timestamp -- **Gradle**: Compares with build.gradle or build.gradle.kts timestamp -- If config older, returns true - -If all checks pass, returns false indicating no generation needed. - -#### Step 4: Execute Config Generation -Detects build tool and constructs appropriate command: -- **Maven**: `mvn liberty:prepare-config` -- **Gradle**: `./gradlew libertyPrepareConfig` or `gradlew.bat libertyPrepareConfig` (Windows) - -Creates ProcessBuilder, executes command, captures output, and checks exit code. Returns ConfigGenerationResult with success status, error message, and duration. - -#### Step 5: Handle Generation Result - -**If Successful:** -- Config file now exists with mock server structure created -- Adds project to processedProjects set -- **Immediately calls SettingsService to reload variables for this workspace** -- Variables are populated from newly generated config -- Proceeds with full diagnostic features - -**If Failed:** -- Creates `.libertyls/prepare-config` directory and saves detailed error log to `build.log` -- Marks project as failed with error message and log file path -- Proceeds to show failure diagnostic to user - -**If Timeout (>5 seconds):** -- Allows generation process to continue in background -- Config will be checked again on next diagnostic trigger or variable access - -#### Step 6: Reload Variables After Successful Generation -After successful config generation, calls SettingsService `populateVariablesForWorkspace()` method. Reads newly generated liberty-plugin-config.xml, extracts directory paths, creates ServerConfigDocument, loads default Liberty properties and custom properties from bootstrap.properties, and stores variables in map. - -#### Step 7: Show Failure Diagnostic (If Generation Failed) -If project marked as failed, creates warning diagnostic at line 1 of server.xml with error summary, resolution steps specific to Maven or Gradle, and clickable link to detailed error log at `.libertyls/prepare-config/build.log`. - -**Maven Resolution Steps:** -1. Ensure liberty-maven-plugin version 3.11 or later -2. Run 'mvn liberty:prepare-config' manually -3. Check pom.xml configuration -4. Verify Maven is installed - -**Gradle Resolution Steps:** -1. Ensure liberty-gradle-plugin version 3.11 or later -2. Run 'gradle libertyPrepareConfig' manually -3. Check build.gradle configuration -4. Verify Gradle is installed - -#### Step 8: Perform Standard Diagnostics -After handling config generation, proceeds with standard Liberty diagnostics: validates XML structure, checks feature names, validates variable references, checks file paths in `` elements, validates configuration values, and checks for deprecated features. All diagnostics use data from mock server structure. - -### C. Variable Access (On-Demand Population) - -#### Step 1: Variable Access Trigger -Any language server feature that needs variables triggers this flow: code completion for `${variable}` references, hover documentation, validation of variable references, or document link resolution. - -#### Step 2: Get Variables for Server XML -Receives server.xml URI, calls LibertyProjectsManager to get workspace folder, checks if variables exist for workspace, and returns cached variables or empty properties. - -#### Step 3: Populate Variables If Needed -Called when variables don't exist for workspace: -- Initializes variables map if null (lazy initialization) -- Calls `ensureConfigIsUpToDate()` to check if regeneration needed -- Reads config file from build output directory (target or build) -- Extracts directory paths (installDirectory, serverDirectory, userDirectory, serverOutputDirectory) -- Creates ServerConfigDocument with extracted paths (all pointing to mock structure) -- Loads default Liberty properties and custom properties from bootstrap.properties in mock server -- Stores variables in map with workspace URI as key - -#### Step 4: Return Variables -Returns properties object with all variables for workspace. Calling feature uses variables for completion, hover, or validation. - -### D. File Change Detection (FileWatchService) - -FileWatchService monitors changes to Liberty configuration files in two locations: - -**1. Source Config Directory** (`src/main/liberty/config/`): -- Monitors for ANY file changes in the source config directory -- When change detected, automatically triggers prepare-config to sync changes to mock server -- After successful regeneration, immediately reloads variables from updated mock server -- Provides seamless developer experience - changes take effect automatically - -**2. Mock Server Directory** (`target/tmp/wlp/usr/servers/{serverName}/` or `build/tmp/wlp/usr/servers/{serverName}/`): -- Monitors for changes to config files in the mock server -- When change detected, reloads variables without regenerating config -- Handles cases where mock server is modified directly - -**Flow when source config file changes:** -1. FileWatchService detects file modification in `src/main/liberty/config/` -2. Triggers prepare-config goal/task to regenerate mock server -3. Maven/Gradle plugin copies updated files from source to mock server -4. Resolves build properties in updated files -5. Generates new liberty-plugin-config.xml -6. Immediately calls SettingsService `populateVariablesForWorkspace()` method -7. Re-reads config and configuration files from updated mock server -8. Updates variable values in workspace variable map -9. Next diagnostic run uses updated values - no IDE restart required - ---- - -## Benefits - -✅ **Zero configuration** - Works immediately after project clone without setup -✅ **Always up-to-date** - Config checked before every use, never stale -✅ **Self-healing** - Automatically fixes stale or missing configs -✅ **No Liberty installation required** - Mock server structure eliminates dependency -✅ **Simpler architecture** - No file watcher complexity for config file -✅ **No race conditions** - Synchronous on-demand checks -✅ **Easier to debug** - Straightforward flow without async events -✅ **Less resource usage** - No continuous monitoring overhead -✅ **Build tool agnostic** - Works with both Maven and Gradle projects diff --git a/docs/prepare-config-scenarios.md b/docs/prepare-config-scenarios.md deleted file mode 100644 index eff77b19a..000000000 --- a/docs/prepare-config-scenarios.md +++ /dev/null @@ -1,326 +0,0 @@ -# Liberty Config Language Server - Common Scenarios - -This document describes common scenarios when using the Liberty Config Language Server with Maven or Gradle projects. - ---- - -## Scenario 1: First Time Opening server.xml - -### Maven Project -1. User opens server.xml in IDE for the first time -2. LemMinX triggers diagnostics -3. Check: liberty-plugin-config.xml doesn't exist in target directory -4. Execute: `mvn liberty:prepare-config` in background with 5-second timeout -5. Maven Plugin executes: - - Creates `target/tmp/wlp/usr/servers/defaultServer/` directory structure - - Copies server.xml from `src/main/liberty/config/` to mock server - - Copies bootstrap.properties to mock server if exists - - Resolves Maven properties in all config files - - Generates `target/liberty-plugin-config.xml` with paths to mock server -6. Config generation completes successfully within timeout -7. Immediately reload variables for this workspace: - - SettingsService reads paths from config (all point to target/tmp) - - Creates ServerConfigDocument with mock server paths - - Loads default Liberty properties from metadata - - Loads custom variables from bootstrap.properties in mock server - - Stores variables in map for this workspace -8. Diagnostics run with full language server features enabled -9. User sees in IDE: - - Code completion for features, variables, file paths - - Hover documentation for elements and attributes - - Error and warning diagnostics for invalid configuration - - Clickable file paths in include elements - - All features work immediately without manual intervention - -### Gradle Project -Same flow as Maven, but: -- Check: liberty-plugin-config.xml doesn't exist in build directory -- Execute: `gradle libertyPrepareConfig` or `./gradlew libertyPrepareConfig` -- Creates `build/tmp/wlp/usr/servers/defaultServer/` directory structure -- Generates `build/liberty-plugin-config.xml` with paths to mock server - ---- - -## Scenario 2: After Clean Build - -### Maven Project (mvn clean) -1. User executes: `mvn clean` from terminal or IDE -2. Maven deletes entire target directory including: - - `target/tmp/` (mock server structure with all config files) - - `target/liberty-plugin-config.xml` (generated config file) -3. User opens server.xml in IDE -4. LemMinX triggers diagnostics -5. Check: liberty-plugin-config.xml doesn't exist in target directory -6. Check: Mock server directory missing -7. Remove project from processedProjects cache: - - Allows regeneration even though previously successful - - Ensures fresh generation after clean -8. Execute: `mvn liberty:prepare-config` -9. Maven Plugin recreates everything from scratch: - - Creates `target/tmp/wlp/usr/servers/defaultServer/` directory - - Copies server.xml from source to mock server - - Copies bootstrap.properties from source to mock server - - Copies all other config files from source to mock server - - Resolves Maven properties in all copied files - - Generates `target/liberty-plugin-config.xml` with mock paths -10. Immediately reload variables after successful generation: - - Reads newly generated config file - - Extracts directory paths pointing to new mock server - - Loads variables from new bootstrap.properties - - Stores variables in map for workspace -11. Full language server features restored automatically -12. User continues working with no manual intervention required -13. All features work as if clean never happened - -### Gradle Project (gradle clean) -Same flow as Maven, but: -- Gradle deletes entire build directory -- Check: liberty-plugin-config.xml doesn't exist in build directory -- Execute: `gradle libertyPrepareConfig` or `./gradlew libertyPrepareConfig` -- Creates `build/tmp/wlp/usr/servers/defaultServer/` directory -- Generates `build/liberty-plugin-config.xml` with mock paths - ---- - -## Scenario 3: After Build File Change - -### Maven Project (pom.xml modified) -1. User modifies pom.xml: - - Adds new dependency - - Changes plugin configuration - - Updates Liberty version - - Modifies server name or directories -2. User saves pom.xml (now newer than liberty-plugin-config.xml) -3. User opens server.xml in IDE -4. LemMinX triggers diagnostics -5. Check: liberty-plugin-config.xml exists in target directory -6. Check: Is config stale (older than pom.xml)? - - Result: YES (pom.xml modified timestamp is newer than config timestamp) -7. Execute: `mvn liberty:prepare-config` to regenerate with updated settings -8. Maven Plugin regenerates config: - - Recreates mock server structure - - Copies config files with updated Maven properties resolved - - Generates new liberty-plugin-config.xml with updated dependencies and settings -9. Immediately reload variables after successful generation: - - Reads new config file - - Extracts updated directory paths - - Loads updated variables from new bootstrap.properties - - Updates variable map for workspace -10. Diagnostics run with updated configuration and variables -11. User sees updated features reflecting new dependencies and configuration -12. No manual intervention required - all automatic - -### Gradle Project (build.gradle modified) -Same flow as Maven, but: -- User modifies build.gradle or build.gradle.kts -- Check: Is config stale (older than build.gradle)? -- Execute: `gradle libertyPrepareConfig` or `./gradlew libertyPrepareConfig` -- Gradle Plugin regenerates config with updated settings - ---- - -## Scenario 4: Config Generation Fails - -### Maven Project -1. User opens server.xml in IDE -2. LemMinX triggers diagnostics -3. Check: liberty-plugin-config.xml doesn't exist in target directory -4. Execute: `mvn liberty:prepare-config` -5. Maven Plugin fails due to: - - Invalid pom.xml syntax - - Missing liberty-maven-plugin configuration - - Incorrect plugin version - - Network error downloading dependencies - - Missing required parameters -6. Capture error output from Maven command: - - Reads all output lines from process - - Captures both standard output and error streams - - Stores complete output for logging -7. Save detailed error log to `.libertyls/prepare-config/build.log`: - - Creates `.libertyls/prepare-config` directory if doesn't exist - - Writes timestamp to log file - - Writes full Maven output to log file - - Writes error messages to log file - - Writes stack traces if available to log file -8. Mark project as failed in LibertyConfigGenerationService: - - Stores error message in failed projects map - - Stores log file path in failed projects map - - Associates failure with workspace URI -9. Warning diagnostic shown in server.xml at line 1: - ``` - Liberty configuration generation failed. Full language server features - are unavailable until this is resolved. - - Error: [Brief error summary from Maven output] - - Resolution steps: - 1. Ensure liberty-maven-plugin version 3.11 or later - 2. Run 'mvn liberty:prepare-config' manually to see full error - 3. Check pom.xml configuration - 4. Verify Maven is installed and accessible - - [View detailed error log](.libertyls/prepare-config/build.log) - ``` -10. User clicks "View detailed error log" link in diagnostic -11. IDE opens `.libertyls/prepare-config/build.log` in editor -12. User reads full error details: - - Sees complete Maven output - - Identifies specific error (e.g., missing plugin version in pom.xml) - - Understands what needs to be fixed -13. User fixes issue in pom.xml (e.g., adds correct plugin version) -14. User saves pom.xml (now newer than failed config attempt) -15. User reopens server.xml or triggers diagnostics -16. Config check detects stale config (pom.xml newer than last attempt) -17. Regenerates config successfully this time: - - Maven plugin executes without errors - - Creates mock server structure - - Generates config file -18. Reloads variables immediately after successful generation -19. Project removed from failed projects list -20. Warning diagnostic automatically disappears from server.xml -21. Full language server features now available -22. User continues working normally - -### Gradle Project -Same flow as Maven, but: -- Execute: `gradle libertyPrepareConfig` or `./gradlew libertyPrepareConfig` -- Gradle Plugin fails due to similar reasons (invalid build.gradle, missing plugin, etc.) -- Warning diagnostic shows Gradle-specific resolution steps: - ``` - Liberty configuration generation failed. Full language server features - are unavailable until this is resolved. - - Error: [Brief error summary from Gradle output] - - Resolution steps: - 1. Ensure liberty-gradle-plugin version 3.11 or later - 2. Run 'gradle libertyPrepareConfig' manually to see full error - 3. Check build.gradle configuration - 4. Verify Gradle is installed and accessible - - [View detailed error log](.libertyls/prepare-config/build.log) - ``` -- User fixes issue in build.gradle or build.gradle.kts -- Regenerates successfully after fix - ---- - -## Scenario 5: Config File Change Detection - -### Maven Project -1. User modifies bootstrap.properties in `src/main/liberty/config/` -2. User adds new variable: `myapp.port=9080` -3. User saves bootstrap.properties -4. FileWatchService detects file modification -5. Listener calls SettingsService `populateVariablesForWorkspace()` method -6. SettingsService checks if config needs regeneration: - - Calls `ensureConfigIsUpToDate()` method - - Checks if pom.xml is newer than liberty-plugin-config.xml - - If stale, regenerates config automatically -7. Re-reads config from `target/liberty-plugin-config.xml` -8. Reads bootstrap.properties from `target/tmp/wlp/usr/servers/defaultServer/bootstrap.properties` -9. Parses configuration files for variable definitions -10. Extracts new variable `myapp.port=9080` -11. Updates variable values in workspace variable map -12. Next diagnostic run uses updated values: - - Variable references in server.xml validated against new values - - Completion suggestions include new variable - - Hover shows new variable value -13. No IDE restart required - changes take effect immediately - -### Gradle Project -Same flow as Maven, but: -- Reads config from `build/liberty-plugin-config.xml` -- Reads bootstrap.properties from `build/tmp/wlp/usr/servers/defaultServer/bootstrap.properties` ---- - -## Scenario 7: Source Config File Modified (Automatic Sync) - -This scenario demonstrates the automatic synchronization when a user modifies a config file in the source directory with the new file watching feature. - -### Maven Project -1. User modifies `src/main/liberty/config/bootstrap.properties` (source directory) -2. User adds new variable: `myapp.database.url=jdbc:mysql://localhost:3306/mydb` -3. User saves bootstrap.properties -4. **FileWatchService automatically detects the change** in `src/main/liberty/config/` directory -5. FileWatchService triggers prepare-config automatically: - - Executes `mvn liberty:prepare-config` in background - - Maven Plugin copies updated bootstrap.properties from source to mock server - - Resolves Maven properties in the updated file - - Generates new liberty-plugin-config.xml -6. After successful regeneration, FileWatchService immediately reloads variables: - - Calls SettingsService `populateVariablesForWorkspace()` method - - Reads updated bootstrap.properties from `target/tmp/wlp/usr/servers/defaultServer/` - - Extracts new variable `myapp.database.url=jdbc:mysql://localhost:3306/mydb` - - Updates variable map for workspace -7. Next diagnostic run (or current if in progress) uses updated values: - - Variable reference `${myapp.database.url}` now resolves correctly - - Code completion suggests new variable - - Hover shows new variable value -8. **No manual intervention required** - all automatic! - -### Gradle Project -Same flow as Maven, but: -- FileWatchService detects change in `src/main/liberty/config/` -- Executes `gradle libertyPrepareConfig` or `./gradlew libertyPrepareConfig` automatically -- Gradle Plugin copies updated file to `build/tmp/wlp/usr/servers/defaultServer/` -- Variables reloaded from updated mock server - -### Key Benefits - -**Seamless Developer Experience:** -- Changes to source config files automatically sync to mock server -- No need to manually run prepare-config -- No need to delete config files or directories -- No need to restart IDE or language server -- Variables update immediately after file save - -**How It Works:** -- FileWatchService monitors `src/main/liberty/config/` directory -- Detects ANY file change (create, modify) in that directory -- Automatically triggers prepare-config to sync changes -- Reloads variables after successful sync -- All happens in background without user intervention - - ---- - -## Scenario 6: Multi-Module Project - -### Maven Multi-Module Project -1. Project structure: - ``` - parent-project/ - ├── pom.xml (parent) - ├── module-a/ - │ ├── pom.xml - │ └── src/main/liberty/config/server.xml - └── module-b/ - ├── pom.xml - └── src/main/liberty/config/server.xml - ``` -2. User opens server.xml in module-a -3. LemMinX discovers workspace and identifies module-a as Liberty project -4. Check: liberty-plugin-config.xml doesn't exist in module-a/target -5. Execute: `mvn liberty:prepare-config` in module-a directory -6. Maven Plugin generates config for module-a: - - Creates `module-a/target/tmp/wlp/usr/servers/defaultServer/` - - Generates `module-a/target/liberty-plugin-config.xml` -7. Variables loaded for module-a workspace -8. User opens server.xml in module-b -9. LemMinX identifies module-b as separate Liberty project -10. Check: liberty-plugin-config.xml doesn't exist in module-b/target -11. Execute: `mvn liberty:prepare-config` in module-b directory -12. Maven Plugin generates config for module-b: - - Creates `module-b/target/tmp/wlp/usr/servers/defaultServer/` - - Generates `module-b/target/liberty-plugin-config.xml` -13. Variables loaded for module-b workspace -14. Each module has independent config and variables -15. Changes in one module don't affect the other - -### Gradle Multi-Module Project -Same flow as Maven, but: -- Project structure uses build.gradle files -- Execute: `gradle libertyPrepareConfig` in each module directory -- Creates `module-a/build/tmp/` and `module-b/build/tmp/` directories -- Generates separate config files in each module's build directory \ No newline at end of file From b9ed527e8d415ef99476d1c5e293204c6ab62089 Mon Sep 17 00:00:00 2001 From: Arun Venmany Date: Thu, 7 May 2026 20:55:49 +0530 Subject: [PATCH 6/9] changes to pass temporary variable directory. defaulted to tmp/liberty-var-cache --- .../wlp/maven/test/app/PrepareConfigIT.java | 48 ++++---- .../test/app/PrepareConfigNoServerInfoIT.java | 36 +++--- .../tools/maven/server/PrepareConfigMojo.java | 110 ++++++++++++------ 3 files changed, 119 insertions(+), 75 deletions(-) diff --git a/liberty-maven-plugin/src/it/prepare-config-it/src/test/java/net/wasdev/wlp/maven/test/app/PrepareConfigIT.java b/liberty-maven-plugin/src/it/prepare-config-it/src/test/java/net/wasdev/wlp/maven/test/app/PrepareConfigIT.java index e30b8573d..18382c0a3 100644 --- a/liberty-maven-plugin/src/it/prepare-config-it/src/test/java/net/wasdev/wlp/maven/test/app/PrepareConfigIT.java +++ b/liberty-maven-plugin/src/it/prepare-config-it/src/test/java/net/wasdev/wlp/maven/test/app/PrepareConfigIT.java @@ -29,6 +29,7 @@ import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; +import io.openliberty.tools.common.plugins.util.PrepareConfigUtil; import org.junit.Assert; import org.junit.Test; import org.w3c.dom.Document; @@ -41,7 +42,8 @@ public class PrepareConfigIT { private static final String PLUGIN_CONFIG_XML = "target/liberty-plugin-config.xml"; - public static final String TARGET_TMP_WLP_USR_SERVERS_TEST_SERVER = "target/tmp/wlp/usr/servers/testServer"; + private static final String TEMP_DIR_NAME = PrepareConfigUtil.DEFAULT_TEMP_DIR_NAME; + public static final String TARGET_TMP_WLP_USR_SERVERS_TEST_SERVER = "target/" + TEMP_DIR_NAME + "/wlp/usr/servers/testServer"; public static final String BOOTSTRAP_PROPERTIES = "bootstrap.properties"; public static final String SERVER_XML = "server.xml"; public static final String LIBERTY_PLUGIN_CONFIG_CONFIG_FILE = "/liberty-plugin-config/configFile"; @@ -146,9 +148,9 @@ public void testNoTempDirectoryLeftBehind() throws Exception { @Test public void testMockLibertyServerStructureCreated() throws Exception { - // Verify that tmp directory structure was created - File tmpDir = new File("target/tmp"); - Assert.assertTrue("tmp directory should exist", tmpDir.exists()); + // Verify that temp directory structure was created + File tmpDir = new File("target/" + TEMP_DIR_NAME); + Assert.assertTrue(TEMP_DIR_NAME + " directory should exist", tmpDir.exists()); File wlpDir = new File(tmpDir, "wlp"); Assert.assertTrue("wlp directory should exist", wlpDir.exists()); @@ -180,35 +182,35 @@ public void testPluginConfigXmlPointsToMockServer() throws Exception { // Verify that liberty-plugin-config.xml has correct directory structure // All directories should point to mock structure in tmp - // installDirectory should point to mock Liberty in tmp + // installDirectory should point to mock Liberty in temp dir String installDir = getXPathValue("/liberty-plugin-config/installDirectory"); Assert.assertNotNull("Install directory should be present", installDir); - Assert.assertTrue("Install directory should point to tmp/wlp", - installDir.contains("tmp") && installDir.endsWith("wlp")); + Assert.assertTrue("Install directory should point to " + TEMP_DIR_NAME + "/wlp", + installDir.contains(TEMP_DIR_NAME) && installDir.endsWith("wlp")); - // userDirectory should point to mock user directory in tmp + // userDirectory should point to mock user directory in temp dir String userDir = getXPathValue("/liberty-plugin-config/userDirectory"); Assert.assertNotNull("User directory should be present", userDir); - Assert.assertTrue("User directory should point to tmp/wlp/usr", - userDir.contains("tmp") && userDir.contains("wlp") && userDir.endsWith("usr")); + Assert.assertTrue("User directory should point to " + TEMP_DIR_NAME + "/wlp/usr", + userDir.contains(TEMP_DIR_NAME) && userDir.contains("wlp") && userDir.endsWith("usr")); - // serverDirectory should point to mock server in tmp + // serverDirectory should point to mock server in temp dir String serverDir = getXPathValue("/liberty-plugin-config/serverDirectory"); Assert.assertNotNull("Server directory should be present", serverDir); - Assert.assertTrue("Server directory should point to mock server in tmp", - serverDir.contains("tmp") && serverDir.contains("testServer")); + Assert.assertTrue("Server directory should point to mock server in " + TEMP_DIR_NAME, + serverDir.contains(TEMP_DIR_NAME) && serverDir.contains("testServer")); - // serverOutputDirectory should point to mock server in tmp + // serverOutputDirectory should point to mock server in temp dir String serverOutputDir = getXPathValue("/liberty-plugin-config/serverOutputDirectory"); Assert.assertNotNull("Server output directory should be present", serverOutputDir); - Assert.assertTrue("Server output directory should point to mock server in tmp", - serverOutputDir.contains("tmp") && serverOutputDir.contains("testServer")); + Assert.assertTrue("Server output directory should point to mock server in " + TEMP_DIR_NAME, + serverOutputDir.contains(TEMP_DIR_NAME) && serverOutputDir.contains("testServer")); // configFile should point to mock server String configFile = getXPathValue(LIBERTY_PLUGIN_CONFIG_CONFIG_FILE); Assert.assertNotNull("Config file should be present", configFile); - Assert.assertTrue("Config file should point to mock server in tmp", - configFile.contains("tmp") && configFile.contains("testServer")); + Assert.assertTrue("Config file should point to mock server in " + TEMP_DIR_NAME, + configFile.contains(TEMP_DIR_NAME) && configFile.contains("testServer")); } /** @@ -266,7 +268,7 @@ public void testPluginConfigXmlContainsUserDirectory() throws Exception { String userDirectory = getXPathValue("/liberty-plugin-config/userDirectory"); Assert.assertNotNull("User directory should be present", userDirectory); Assert.assertTrue("User directory should point to mock user directory", - userDirectory.contains("tmp") && userDirectory.contains("usr")); + userDirectory.contains(TEMP_DIR_NAME) && userDirectory.contains("usr")); } @Test @@ -274,7 +276,7 @@ public void testPluginConfigXmlContainsServerOutputDirectory() throws Exception String serverOutputDirectory = getXPathValue("/liberty-plugin-config/serverOutputDirectory"); Assert.assertNotNull("Server output directory should be present", serverOutputDirectory); Assert.assertTrue("Server output directory should point to mock server", - serverOutputDirectory.contains("tmp") && serverOutputDirectory.contains("testServer")); + serverOutputDirectory.contains(TEMP_DIR_NAME) && serverOutputDirectory.contains("testServer")); } @Test @@ -330,20 +332,20 @@ public void testPluginConfigXmlContainsBuildDirectory() throws Exception { @Test public void testMockServerStructureIsComplete() throws Exception { // Comprehensive check of the entire mock server structure - File tmpDir = new File("target/tmp"); + File tmpDir = new File("target/" + TEMP_DIR_NAME); File wlpDir = new File(tmpDir, "wlp"); File usrDir = new File(wlpDir, "usr"); File serversDir = new File(usrDir, "servers"); File serverDir = new File(serversDir, "testServer"); - Assert.assertTrue("tmp directory should be a directory", tmpDir.isDirectory()); + Assert.assertTrue(TEMP_DIR_NAME + " directory should be a directory", tmpDir.isDirectory()); Assert.assertTrue("wlp directory should be a directory", wlpDir.isDirectory()); Assert.assertTrue("usr directory should be a directory", usrDir.isDirectory()); Assert.assertTrue("servers directory should be a directory", serversDir.isDirectory()); Assert.assertTrue("testServer directory should be a directory", serverDir.isDirectory()); // Verify the structure matches Liberty's expected layout - Assert.assertEquals("wlp should be child of tmp", tmpDir, wlpDir.getParentFile()); + Assert.assertEquals("wlp should be child of " + TEMP_DIR_NAME, tmpDir, wlpDir.getParentFile()); Assert.assertEquals("usr should be child of wlp", wlpDir, usrDir.getParentFile()); Assert.assertEquals("servers should be child of usr", usrDir, serversDir.getParentFile()); Assert.assertEquals("testServer should be child of servers", serversDir, serverDir.getParentFile()); diff --git a/liberty-maven-plugin/src/it/prepare-config-no-server-info-it/src/test/java/net/wasdev/wlp/maven/test/app/PrepareConfigNoServerInfoIT.java b/liberty-maven-plugin/src/it/prepare-config-no-server-info-it/src/test/java/net/wasdev/wlp/maven/test/app/PrepareConfigNoServerInfoIT.java index 4bdf42dfb..66d400919 100644 --- a/liberty-maven-plugin/src/it/prepare-config-no-server-info-it/src/test/java/net/wasdev/wlp/maven/test/app/PrepareConfigNoServerInfoIT.java +++ b/liberty-maven-plugin/src/it/prepare-config-no-server-info-it/src/test/java/net/wasdev/wlp/maven/test/app/PrepareConfigNoServerInfoIT.java @@ -29,6 +29,7 @@ import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; +import io.openliberty.tools.common.plugins.util.PrepareConfigUtil; import org.junit.Assert; import org.junit.Test; import org.w3c.dom.Document; @@ -43,6 +44,7 @@ public class PrepareConfigNoServerInfoIT { private static final String PLUGIN_CONFIG_XML = "target/liberty-plugin-config.xml"; + private static final String TEMP_DIR_NAME = PrepareConfigUtil.DEFAULT_TEMP_DIR_NAME; @Test public void testPluginConfigXmlExists() throws Exception { @@ -136,9 +138,9 @@ public void testPluginConfigXmlContainsApplicationFilename() throws Exception { @Test public void testMockLibertyServerStructureCreated() throws Exception { - // Verify that tmp directory structure was created even with includeServerInfo=false - File tmpDir = new File("target/tmp"); - Assert.assertTrue("tmp directory should exist", tmpDir.exists()); + // Verify that temp directory structure was created even with includeServerInfo=false + File tmpDir = new File("target/" + TEMP_DIR_NAME); + Assert.assertTrue(TEMP_DIR_NAME + " directory should exist", tmpDir.exists()); File wlpDir = new File(tmpDir, "wlp"); Assert.assertTrue("wlp directory should exist", wlpDir.exists()); @@ -157,7 +159,7 @@ public void testMockLibertyServerStructureCreated() throws Exception { public void testConfigFilesNotCopiedToMockServer() throws Exception { // When includeServerInfo=false, config files should still be copied // because copyConfigFiles is called before the includeServerInfo check - File mockServerDir = new File("target/tmp/wlp/usr/servers/testServer"); + File mockServerDir = new File("target/" + TEMP_DIR_NAME + "/wlp/usr/servers/testServer"); File serverXml = new File(mockServerDir, "server.xml"); Assert.assertTrue("server.xml should be copied to mock server", serverXml.exists()); @@ -166,31 +168,31 @@ public void testConfigFilesNotCopiedToMockServer() throws Exception { @Test public void testPluginConfigXmlPointsToMockServer() throws Exception { // Verify that liberty-plugin-config.xml has correct directory structure - // All directories should point to mock structure in tmp + // All directories should point to mock structure in temp dir - // installDirectory should point to mock Liberty in tmp + // installDirectory should point to mock Liberty in temp dir String installDir = getXPathValue("/liberty-plugin-config/installDirectory"); Assert.assertNotNull("Install directory should be present", installDir); - Assert.assertTrue("Install directory should point to tmp/wlp", - installDir.contains("tmp") && installDir.endsWith("wlp")); + Assert.assertTrue("Install directory should point to " + TEMP_DIR_NAME + "/wlp", + installDir.contains(TEMP_DIR_NAME) && installDir.endsWith("wlp")); - // userDirectory should point to mock user directory in tmp + // userDirectory should point to mock user directory in temp dir String userDir = getXPathValue("/liberty-plugin-config/userDirectory"); Assert.assertNotNull("User directory should be present", userDir); - Assert.assertTrue("User directory should point to tmp/wlp/usr", - userDir.contains("tmp") && userDir.contains("wlp") && userDir.endsWith("usr")); + Assert.assertTrue("User directory should point to " + TEMP_DIR_NAME + "/wlp/usr", + userDir.contains(TEMP_DIR_NAME) && userDir.contains("wlp") && userDir.endsWith("usr")); - // serverDirectory should point to mock server in tmp + // serverDirectory should point to mock server in temp dir String serverDir = getXPathValue("/liberty-plugin-config/serverDirectory"); Assert.assertNotNull("Server directory should be present", serverDir); - Assert.assertTrue("Server directory should point to mock server in tmp", - serverDir.contains("tmp") && serverDir.contains("testServer")); + Assert.assertTrue("Server directory should point to mock server in " + TEMP_DIR_NAME, + serverDir.contains(TEMP_DIR_NAME) && serverDir.contains("testServer")); - // serverOutputDirectory should point to mock server in tmp + // serverOutputDirectory should point to mock server in temp dir String serverOutputDir = getXPathValue("/liberty-plugin-config/serverOutputDirectory"); Assert.assertNotNull("Server output directory should be present", serverOutputDir); - Assert.assertTrue("Server output directory should point to mock server in tmp", - serverOutputDir.contains("tmp") && serverOutputDir.contains("testServer")); + Assert.assertTrue("Server output directory should point to mock server in " + TEMP_DIR_NAME, + serverOutputDir.contains(TEMP_DIR_NAME) && serverOutputDir.contains("testServer")); } @Test diff --git a/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/PrepareConfigMojo.java b/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/PrepareConfigMojo.java index 41c5eb22a..307d59eac 100644 --- a/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/PrepareConfigMojo.java +++ b/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/PrepareConfigMojo.java @@ -31,50 +31,41 @@ /** * Prepare Liberty configuration and generate liberty-plugin-config.xml without - * creating the server. This lightweight goal evaluates project configuration + * creating the server. This lightweight goal evaluates project configuration * and generates metadata needed by IDE tools and language servers. - * + * *

* This goal is designed to be fast and non-invasive, making it suitable for * automatic execution when Liberty configuration files are opened in an IDE. *

- * + * *

* The generated liberty-plugin-config.xml file contains: *

*
    *
  • Project structure information (directories, dependencies)
  • - *
  • Server configuration file locations (when includeServerInfo=true)
  • + *
  • Server configuration file locations
  • *
  • Liberty installation paths (when Liberty is already installed)
  • *
- * - *

- * Parameters: - *

- *
    - *
  • includeServerInfo (default: true) - Include server-specific - * configuration file paths in the generated config. Set to false for faster - * execution when only basic project information is needed.
  • - *
- * + * *

* Note: This goal does NOT install Liberty or create a server. If you need * Liberty installed for full variable resolution in language servers, use the * liberty:create or liberty:dev goals first. *

- * + * *

* Usage Examples: *

- * + * *
  * {@code
- * 
+ * 
  * mvn liberty:prepare-config
- * 
- * 
- * mvn liberty:prepare-config -DincludeServerInfo=false
- * 
+ *
+ * 
+ * mvn liberty:prepare-config -DprepareConfigTempDir=my-temp
+ *
  * 
  * mvn liberty:create
  * mvn liberty:prepare-config
@@ -85,15 +76,20 @@
 public class PrepareConfigMojo extends PluginConfigSupport {
 
     /**
-     * Whether to include server-specific information in the generated config.
-     * When true, includes server.xml, bootstrap.properties, jvm.options, etc.
-     * When false, only includes project and build metadata.
+     * Name of the temporary directory used for mock Liberty server structures.
+     * This directory is created under the build output directory (target/).
+     * Default value is "tmp/liberty-var-cache".
+     *
+     * 

Example: If set to "my-temp", the mock server will be created at: + * target/my-temp/wlp/usr/servers/{serverName}

*/ - @Parameter(property = "includeServerInfo", defaultValue = "true") - private boolean includeServerInfo; + @Parameter(property = "prepareConfigTempDir", defaultValue = "tmp/liberty-var-cache") + private String prepareConfigTempDir; @Override public void execute() throws MojoExecutionException { + // Set flag to skip server config setup in init() to avoid Liberty runtime download + skipServerConfigSetup = true; init(); if (skip) { @@ -101,16 +97,53 @@ public void execute() throws MojoExecutionException { return; } + // Set up minimal required fields that were skipped by skipServerConfigSetup + initializeMinimalServerConfig(); + doPrepareConfig(); } + /** + * Initialize minimal server configuration needed for prepare-config goal. + * This sets up mock paths without downloading Liberty runtime. + */ + private void initializeMinimalServerConfig() { + // Set server name if not already set + if (serverName == null) { + serverName = "defaultServer"; + } + + // Validate and use the configured temp directory name + String tempDirName = (prepareConfigTempDir != null && !prepareConfigTempDir.trim().isEmpty()) + ? prepareConfigTempDir.trim() + : PrepareConfigUtil.DEFAULT_TEMP_DIR_NAME; + + // Set up mock Liberty directory structure + File buildDirectory = new File(project.getBuild().getDirectory()); + installDirectory = PrepareConfigUtil.getMockInstallDirectory(buildDirectory, tempDirName); + userDirectory = PrepareConfigUtil.getMockUserDirectory(buildDirectory, tempDirName); + File serversDirectory = PrepareConfigUtil.getMockServersDirectory(buildDirectory, tempDirName); + serverDirectory = new File(serversDirectory, serverName); + outputDirectory = serversDirectory; + + getLog().debug("prepare-config: Using mock Liberty paths (no runtime download)"); + getLog().debug(" tempDirectory: " + tempDirName); + getLog().debug(" installDirectory: " + installDirectory); + getLog().debug(" serverDirectory: " + serverDirectory); + } + private void doPrepareConfig() throws MojoExecutionException { getLog().info("Preparing Liberty configuration..."); try { + // Validate and use the configured temp directory name + String tempDirName = (prepareConfigTempDir != null && !prepareConfigTempDir.trim().isEmpty()) + ? prepareConfigTempDir.trim() + : PrepareConfigUtil.DEFAULT_TEMP_DIR_NAME; + // Create mock Liberty server structure using common utility File buildDirectory = new File(project.getBuild().getDirectory()); - File mockServerDir = PrepareConfigUtil.createMockLibertyServerStructure(buildDirectory, serverName); + File mockServerDir = PrepareConfigUtil.createMockLibertyServerStructure(buildDirectory, serverName, tempDirName); // Temporarily set serverDirectory to mock location for config file copying File originalServerDir = serverDirectory; @@ -128,7 +161,8 @@ private void doPrepareConfig() throws MojoExecutionException { } // Generate liberty-plugin-config.xml with paths pointing to mock server - File configFile = exportParametersToXml(includeServerInfo); + // Always include server info (server.xml, bootstrap.properties, etc.) + File configFile = exportParametersToXml(true); getLog().info(MessageFormat.format("Liberty configuration file generated: {0}", configFile.getAbsolutePath())); getLog().info(MessageFormat.format("Mock Liberty server structure created: {0}", @@ -141,17 +175,23 @@ private void doPrepareConfig() throws MojoExecutionException { /** * Override to generate liberty-plugin-config.xml pointing to mock server structure. * All directories (installDirectory, userDirectory, serverDirectory, serverOutputDirectory) - * are set to mock locations in target/tmp/wlp/usr/servers/{serverName}. + * are set to mock locations in target/{tempDirName}/wlp/usr/servers/{serverName}. + * Server information is always included in the generated configuration. */ @Override protected File exportParametersToXml(boolean includeServerInfo) throws IOException, ParserConfigurationException, TransformerException { + // Validate and use the configured temp directory name + String tempDirName = (prepareConfigTempDir != null && !prepareConfigTempDir.trim().isEmpty()) + ? prepareConfigTempDir.trim() + : PrepareConfigUtil.DEFAULT_TEMP_DIR_NAME; + // Build mock Liberty directory structure paths using common utility File buildDirectory = new File(project.getBuild().getDirectory()); - File mockInstallDir = PrepareConfigUtil.getMockInstallDirectory(buildDirectory); - File mockUserDir = PrepareConfigUtil.getMockUserDirectory(buildDirectory); - File mockServersDir = PrepareConfigUtil.getMockServersDirectory(buildDirectory); - File mockServerDir = PrepareConfigUtil.getMockServerDirectory(buildDirectory, serverName); + File mockInstallDir = PrepareConfigUtil.getMockInstallDirectory(buildDirectory, tempDirName); + File mockUserDir = PrepareConfigUtil.getMockUserDirectory(buildDirectory, tempDirName); + File mockServersDir = PrepareConfigUtil.getMockServersDirectory(buildDirectory, tempDirName); + File mockServerDir = PrepareConfigUtil.getMockServerDirectory(buildDirectory, serverName, tempDirName); // Save original values to restore after XML generation File originalInstallDir = installDirectory; @@ -168,8 +208,8 @@ protected File exportParametersToXml(boolean includeServerInfo) // Set outputDirectory = mockServersDir to get mockServersDir/serverName = mockServerDir outputDirectory = mockServersDir; - // Call parent implementation with mock directories - return super.exportParametersToXml(includeServerInfo); + // Call parent implementation with mock directories, always including server info + return super.exportParametersToXml(true); } finally { // Restore original values From 36ae2ff123f04917c7cd4473ade2afa5d1f12191 Mon Sep 17 00:00:00 2001 From: Arun Venmany Date: Thu, 7 May 2026 20:55:49 +0530 Subject: [PATCH 7/9] changes to pass temporary variable directory. defaulted to tmp/liberty-var-cache --- docs/prepare-config.md | 44 ++++++++++++++---------------------------- 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/docs/prepare-config.md b/docs/prepare-config.md index 5f24c9e9e..85856e918 100644 --- a/docs/prepare-config.md +++ b/docs/prepare-config.md @@ -1,8 +1,8 @@ -## prepare-config +l## prepare-config --- -Prepare Liberty configuration and generate `liberty-plugin-config.xml` with a mock Liberty server structure. This lightweight goal evaluates project configuration, creates a temporary Liberty server structure in `.libertyls` folder, copies all configuration files, and generates metadata needed by IDE tools and language servers. +Prepare Liberty configuration and generate `liberty-plugin-config.xml` with a mock Liberty server structure. This lightweight goal evaluates project configuration, creates a temporary Liberty server structure in a configurable temporary directory, copies all configuration files, and generates metadata needed by IDE tools and language servers. This goal is particularly useful for: - Enabling IDE support for Liberty configuration files (server.xml, bootstrap.properties, server.env) @@ -12,7 +12,7 @@ This goal is particularly useful for: - Creating a mock Liberty server structure for language server integration **What this goal does:** -1. Creates a mock Liberty server structure in `target/.libertyls/wlp/usr/servers/{serverName}/` +1. Creates a mock Liberty server structure in `target/tmp/liberty-var-cache/wlp/usr/servers/{serverName}/` (configurable) 2. Copies all configuration files (server.xml, bootstrap.properties, server.env, jvm.options, etc.) to the mock server 3. Generates `liberty-plugin-config.xml` pointing to the mock server structure @@ -51,11 +51,11 @@ mvn liberty:prepare-config ### Configuration Parameters -The `prepare-config` goal supports the following configuration parameter in addition to the [common parameters](common-parameters.md) and [common server parameters](common-server-parameters.md): +The `prepare-config` goal supports the following configuration parameters in addition to the [common parameters](common-parameters.md) and [common server parameters](common-server-parameters.md): | Parameter | Description | Required | Default | | --------- | ----------- | -------- | ------- | -| includeServerInfo | Whether to include server-specific information in the generated config. When `true`, includes `server.xml`, `bootstrap.properties`, `jvm.options`, etc. When `false`, only includes project and build metadata. | No | `true` | +| prepareConfigTempDir | Name of the temporary directory used for mock Liberty server structures. This directory is created under the build output directory (`target/` for Maven, `build/` for Gradle). | No | `tmp/liberty-var-cache` | --- @@ -71,15 +71,15 @@ mvn liberty:prepare-config This will create `target/liberty-plugin-config.xml` with project metadata, dependencies, and configuration file references. -#### Example 2: Generate minimal configuration +#### Example 2: Custom temporary directory -Generate only project metadata without server-specific information: +Use a custom temporary directory name: ```bash -mvn liberty:prepare-config -DincludeServerInfo=false +mvn liberty:prepare-config -DprepareConfigTempDir=my-temp-dir ``` -This creates a minimal configuration file with just project and build information, and executes faster. +This will create the mock server structure in `target/my-temp-dir/wlp/usr/servers/{serverName}/` instead of the default location. #### Example 3: IDE integration @@ -97,9 +97,6 @@ Configure the goal to run automatically during project initialization: prepare-config - - true - @@ -111,9 +108,9 @@ Configure the goal to run automatically during project initialization: The `prepare-config` goal generates: -1. **Mock Liberty Server Structure** in `target/.libertyls/`: +1. **Mock Liberty Server Structure** in `target/tmp/liberty-var-cache/` (or custom directory): ``` - target/.libertyls/ + target/tmp/liberty-var-cache/ └── wlp/ └── usr/ └── servers/ @@ -127,17 +124,14 @@ The `prepare-config` goal generates: 2. **Configuration Metadata File** `target/liberty-plugin-config.xml` containing: -**Always included:** -- Install directory (points to `target/.libertyls/wlp`) -- User directory (points to `target/.libertyls/wlp/usr`) -- Server directory (points to `target/.libertyls/wlp/usr/servers/{serverName}`) +- Install directory (points to `target/tmp/liberty-var-cache/wlp`) +- User directory (points to `target/tmp/liberty-var-cache/wlp/usr`) +- Server directory (points to `target/tmp/liberty-var-cache/wlp/usr/servers/{serverName}`) - Server name and output directory paths - Project type (packaging) - Active build profiles - Project compile dependencies - Aggregator parent information (for multi-module projects) - -**Included when `includeServerInfo=true`:** - Configuration directory - Server configuration file path (in mock server) - Bootstrap properties file path (in mock server) @@ -205,7 +199,7 @@ mvn liberty:prepare-config | Goal | Liberty Install | Server Creation | Mock Server Structure | Config Files Copied | Use Case | |------|----------------|-----------------|----------------------|---------------------|----------| -| `prepare-config` | No | No | Yes (in .libertyls) | Yes (to mock server) | Generate config metadata and mock structure for tools | +| `prepare-config` | No | No | Yes (in liberty-var-cache) | Yes (to mock server) | Generate config metadata and mock structure for tools | | `create` | Yes | Yes | No | Yes (to real server) | Create and configure Liberty server | | `install-server` | Yes | No | No | No | Install Liberty runtime only | | `dev` | Yes | Yes | No | Yes (to real server) | Development mode with hot reload | @@ -258,14 +252,6 @@ mvn liberty:prepare-config -X The debug output will show the exact location where the file is being written. -#### Missing server information - -If server-specific information is missing, ensure `includeServerInfo=true`: - -```bash -mvn liberty:prepare-config -DincludeServerInfo=true -``` - #### Variable resolution not working in IDE Variable resolution requires Liberty to be installed. Run: From cd04b5cfb744e0d309f7eb7072d132e3d53a0ab4 Mon Sep 17 00:00:00 2001 From: Arun Venmany Date: Fri, 8 May 2026 10:01:36 +0530 Subject: [PATCH 8/9] using buildDIr/.libertyls-var-cache for temporary directory --- docs/prepare-config.md | 223 ++---------------- .../tools/maven/server/PrepareConfigMojo.java | 44 ++-- 2 files changed, 45 insertions(+), 222 deletions(-) diff --git a/docs/prepare-config.md b/docs/prepare-config.md index 85856e918..bca8c613b 100644 --- a/docs/prepare-config.md +++ b/docs/prepare-config.md @@ -1,34 +1,32 @@ -l## prepare-config +## prepare-config --- -Prepare Liberty configuration and generate `liberty-plugin-config.xml` with a mock Liberty server structure. This lightweight goal evaluates project configuration, creates a temporary Liberty server structure in a configurable temporary directory, copies all configuration files, and generates metadata needed by IDE tools and language servers. - -This goal is particularly useful for: -- Enabling IDE support for Liberty configuration files (server.xml, bootstrap.properties, server.env) -- Providing configuration metadata to language servers for code completion and diagnostics -- Supporting Liberty Tools and other IDE extensions -- Quick configuration validation without full project build -- Creating a mock Liberty server structure for language server integration +Prepare Liberty configuration and generate `liberty-plugin-config.xml` with a mock Liberty server structure. This lightweight goal creates a temporary Liberty server structure, copies configuration files, and generates metadata needed by IDE tools and language servers. **What this goal does:** -1. Creates a mock Liberty server structure in `target/tmp/liberty-var-cache/wlp/usr/servers/{serverName}/` (configurable) +1. Creates a mock Liberty server structure in `target/.libertyls-var-cache/wlp/usr/servers/{serverName}/` (configurable) 2. Copies all configuration files (server.xml, bootstrap.properties, server.env, jvm.options, etc.) to the mock server 3. Generates `liberty-plugin-config.xml` pointing to the mock server structure -The goal does NOT install Liberty runtime. It only creates a minimal directory structure that mimics a Liberty server and copies configuration files. +**Note:** This goal does NOT install Liberty runtime. It only creates a minimal directory structure and copies configuration files. --- ### Usage -The `prepare-config` goal is typically used in IDE scenarios where you need configuration metadata before building the project: +Run directly from the command line: + +```bash +mvn liberty:prepare-config +``` + +Or configure it to run automatically: ```xml io.openliberty.tools liberty-maven-plugin - 3.12.1-SNAPSHOT prepare-config @@ -41,39 +39,13 @@ The `prepare-config` goal is typically used in IDE scenarios where you need conf ``` -Or run it directly from the command line: - -```bash -mvn liberty:prepare-config -``` - --- -### Configuration Parameters - -The `prepare-config` goal supports the following configuration parameters in addition to the [common parameters](common-parameters.md) and [common server parameters](common-server-parameters.md): +### Configuration -| Parameter | Description | Required | Default | -| --------- | ----------- | -------- | ------- | -| prepareConfigTempDir | Name of the temporary directory used for mock Liberty server structures. This directory is created under the build output directory (`target/` for Maven, `build/` for Gradle). | No | `tmp/liberty-var-cache` | +The goal uses the [common parameters](common-parameters.md) and [common server parameters](common-server-parameters.md). ---- - -### Examples - -#### Example 1: Basic usage (default) - -Generate configuration metadata with server information: - -```bash -mvn liberty:prepare-config -``` - -This will create `target/liberty-plugin-config.xml` with project metadata, dependencies, and configuration file references. - -#### Example 2: Custom temporary directory - -Use a custom temporary directory name: +The temporary directory for the mock server structure defaults to `.libertyls-var-cache` (a hidden directory). To override this, use the system property: ```bash mvn liberty:prepare-config -DprepareConfigTempDir=my-temp-dir @@ -81,36 +53,15 @@ mvn liberty:prepare-config -DprepareConfigTempDir=my-temp-dir This will create the mock server structure in `target/my-temp-dir/wlp/usr/servers/{serverName}/` instead of the default location. -#### Example 3: IDE integration - -Configure the goal to run automatically during project initialization: - -```xml - - io.openliberty.tools - liberty-maven-plugin - 3.12.1-SNAPSHOT - - - prepare-config-on-init - initialize - - prepare-config - - - - -``` - --- -### Generated Configuration File and Mock Server Structure +### Generated Files -The `prepare-config` goal generates: +The goal generates: -1. **Mock Liberty Server Structure** in `target/tmp/liberty-var-cache/` (or custom directory): +1. **Mock Liberty Server Structure** in `target/.libertyls-var-cache/`: ``` - target/tmp/liberty-var-cache/ + target/.libertyls-var-cache/ └── wlp/ └── usr/ └── servers/ @@ -118,151 +69,26 @@ The `prepare-config` goal generates: ├── server.xml ├── bootstrap.properties ├── server.env - ├── jvm.options - └── (other config files) + └── jvm.options ``` -2. **Configuration Metadata File** `target/liberty-plugin-config.xml` containing: - -- Install directory (points to `target/tmp/liberty-var-cache/wlp`) -- User directory (points to `target/tmp/liberty-var-cache/wlp/usr`) -- Server directory (points to `target/tmp/liberty-var-cache/wlp/usr/servers/{serverName}`) -- Server name and output directory paths -- Project type (packaging) -- Active build profiles -- Project compile dependencies -- Aggregator parent information (for multi-module projects) -- Configuration directory -- Server configuration file path (in mock server) -- Bootstrap properties file path (in mock server) -- JVM options file path (in mock server) -- Server environment file path (in mock server) -- Applications directory (`apps` or `dropins`) -- Loose application configuration -- Strip version settings -- Application filename +2. **Configuration Metadata File** `target/liberty-plugin-config.xml` containing project metadata, dependencies, and configuration file paths for IDE tools and language servers. --- ### Use Cases -#### 1. IDE Language Server Support - -IDEs using Liberty language servers can invoke this goal to get configuration metadata: - -```bash -mvn liberty:prepare-config -``` - -The generated `liberty-plugin-config.xml` provides language servers with information needed to offer: -- Code completion for Liberty configuration -- Validation of server configuration -- Quick fixes and diagnostics -- Custom file path resolution (for non-standard locations) - -**For full variable resolution features**, first install Liberty using `liberty:create`: - -```bash -mvn liberty:create -mvn liberty:prepare-config -``` +- **IDE Language Server Support**: Provides metadata for code completion, validation, and diagnostics in Liberty configuration files +- **Quick Configuration Validation**: Validate configuration without full project build +- **CI/CD Integration**: Generate configuration metadata early in the pipeline -#### 2. CI/CD Pipeline Validation - -Validate Liberty configuration early in the pipeline without full build: - -```bash -mvn liberty:prepare-config -# Parse and validate liberty-plugin-config.xml -``` - -#### 3. Multi-Module Project Setup - -For multi-module projects, run at the parent level to prepare configuration for all modules: - -```bash -mvn liberty:prepare-config -pl :module-name -``` - -#### 4. Pre-Build Configuration Analysis - -Analyze project configuration before committing to a full build: - -```bash -mvn liberty:prepare-config -# Analyze target/liberty-plugin-config.xml for issues -``` - ---- - -### Comparison with Other Goals - -| Goal | Liberty Install | Server Creation | Mock Server Structure | Config Files Copied | Use Case | -|------|----------------|-----------------|----------------------|---------------------|----------| -| `prepare-config` | No | No | Yes (in liberty-var-cache) | Yes (to mock server) | Generate config metadata and mock structure for tools | -| `create` | Yes | Yes | No | Yes (to real server) | Create and configure Liberty server | -| `install-server` | Yes | No | No | No | Install Liberty runtime only | -| `dev` | Yes | Yes | No | Yes (to real server) | Development mode with hot reload | - ---- - -### Performance Considerations - -The `prepare-config` goal is designed to be lightweight and fast: - -- **Typical execution time**: ~1-2 seconds -- **No Liberty download**: Does not download or install Liberty -- **No server creation**: Does not create server directories -- **Minimal I/O**: Only reads Maven configuration and writes one XML file - -This makes it ideal for IDE integration where responsiveness is critical. - ---- - -### Language Server Integration - -The `prepare-config` goal is designed to work with two Liberty language servers: - -#### 1. lemminx-liberty (XML Language Server) -Provides features for `server.xml` and related XML configuration files: -- Feature completion and validation -- Configuration element completion -- Variable resolution (requires Liberty installation) -- Hover documentation - -#### 2. liberty-ls (Properties Language Server) -Provides features for `bootstrap.properties` and `server.env` files: -- Property name completion -- Property value validation -- Custom file path detection - -**Note**: For full variable resolution in `server.xml`, Liberty must be installed first using `liberty:create` or `liberty:dev`. The `prepare-config` goal will include the Liberty installation paths in the generated config if Liberty is already present. - ---- - -### Troubleshooting - -#### Configuration file not generated - -Check that the goal executed successfully: - -```bash -mvn liberty:prepare-config -X -``` - -The debug output will show the exact location where the file is being written. - -#### Variable resolution not working in IDE - -Variable resolution requires Liberty to be installed. Run: +**For full variable resolution features**, install Liberty first: ```bash mvn liberty:create mvn liberty:prepare-config ``` -This will install Liberty and generate the config file with Liberty installation paths. - --- ### See Also @@ -270,5 +96,4 @@ This will install Liberty and generate the config file with Liberty installation - [Common Parameters](common-parameters.md) - [Common Server Parameters](common-server-parameters.md) - [create goal](create.md) - Install Liberty and create server -- [install-server goal](install-server.md) - Install Liberty runtime only - [dev goal](dev.md) - Development mode with hot reload \ No newline at end of file diff --git a/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/PrepareConfigMojo.java b/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/PrepareConfigMojo.java index 307d59eac..9feaceb33 100644 --- a/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/PrepareConfigMojo.java +++ b/liberty-maven-plugin/src/main/java/io/openliberty/tools/maven/server/PrepareConfigMojo.java @@ -75,17 +75,6 @@ @Mojo(name = "prepare-config", requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, threadSafe = true) public class PrepareConfigMojo extends PluginConfigSupport { - /** - * Name of the temporary directory used for mock Liberty server structures. - * This directory is created under the build output directory (target/). - * Default value is "tmp/liberty-var-cache". - * - *

Example: If set to "my-temp", the mock server will be created at: - * target/my-temp/wlp/usr/servers/{serverName}

- */ - @Parameter(property = "prepareConfigTempDir", defaultValue = "tmp/liberty-var-cache") - private String prepareConfigTempDir; - @Override public void execute() throws MojoExecutionException { // Set flag to skip server config setup in init() to avoid Liberty runtime download @@ -113,10 +102,13 @@ private void initializeMinimalServerConfig() { serverName = "defaultServer"; } - // Validate and use the configured temp directory name - String tempDirName = (prepareConfigTempDir != null && !prepareConfigTempDir.trim().isEmpty()) - ? prepareConfigTempDir.trim() - : PrepareConfigUtil.DEFAULT_TEMP_DIR_NAME; + // Check for system property override, otherwise use default + String tempDirName = System.getProperty("prepareConfigTempDir"); + if (tempDirName == null || tempDirName.trim().isEmpty()) { + tempDirName = PrepareConfigUtil.DEFAULT_TEMP_DIR_NAME; + } else { + tempDirName = tempDirName.trim(); + } // Set up mock Liberty directory structure File buildDirectory = new File(project.getBuild().getDirectory()); @@ -136,10 +128,13 @@ private void doPrepareConfig() throws MojoExecutionException { getLog().info("Preparing Liberty configuration..."); try { - // Validate and use the configured temp directory name - String tempDirName = (prepareConfigTempDir != null && !prepareConfigTempDir.trim().isEmpty()) - ? prepareConfigTempDir.trim() - : PrepareConfigUtil.DEFAULT_TEMP_DIR_NAME; + // Check for system property override, otherwise use default + String tempDirName = System.getProperty("prepareConfigTempDir"); + if (tempDirName == null || tempDirName.trim().isEmpty()) { + tempDirName = PrepareConfigUtil.DEFAULT_TEMP_DIR_NAME; + } else { + tempDirName = tempDirName.trim(); + } // Create mock Liberty server structure using common utility File buildDirectory = new File(project.getBuild().getDirectory()); @@ -181,10 +176,13 @@ private void doPrepareConfig() throws MojoExecutionException { @Override protected File exportParametersToXml(boolean includeServerInfo) throws IOException, ParserConfigurationException, TransformerException { - // Validate and use the configured temp directory name - String tempDirName = (prepareConfigTempDir != null && !prepareConfigTempDir.trim().isEmpty()) - ? prepareConfigTempDir.trim() - : PrepareConfigUtil.DEFAULT_TEMP_DIR_NAME; + // Check for system property override, otherwise use default + String tempDirName = System.getProperty("prepareConfigTempDir"); + if (tempDirName == null || tempDirName.trim().isEmpty()) { + tempDirName = PrepareConfigUtil.DEFAULT_TEMP_DIR_NAME; + } else { + tempDirName = tempDirName.trim(); + } // Build mock Liberty directory structure paths using common utility File buildDirectory = new File(project.getBuild().getDirectory()); From 574ed065a0a7727bbe19b4a7fb7f5242317b1afe Mon Sep 17 00:00:00 2001 From: Arun Venmany Date: Thu, 14 May 2026 09:43:21 +0530 Subject: [PATCH 9/9] changing workflow to point to ci.common branch --- .github/workflows/maven.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index e95bc5fbf..efdbd1dc2 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -7,7 +7,7 @@ on: push: branches: '**' pull_request: - branches: [ main ] + branches: [ main, prepare-config] env: # server-config-props-it fix @@ -98,9 +98,9 @@ jobs: - name: Checkout ci.common uses: actions/checkout@v3 with: - repository: venmanyarun/ci.common + repository: OpenLiberty/ci.common path: ci.common - ref: prepare_config_poc + ref: prepare-config - name: Checkout ci.ant uses: actions/checkout@v3 with: @@ -207,7 +207,7 @@ jobs: - name: Clone ci.ant and ci.common repos to github.workspace run: | echo ${{github.workspace}} - git clone https://github.com/venmanyarun/ci.common.git --branch prepare_config_poc --single-branch ${{github.workspace}}/ci.common + git clone https://github.com/OpenLiberty/ci.common.git --branch prepare-config --single-branch ${{github.workspace}}/ci.common git clone https://github.com/OpenLiberty/ci.ant.git ${{github.workspace}}/ci.ant - name: Set up Maven uses: stCarolas/setup-maven@v4.5