Skip to content

Commit 29c07e9

Browse files
authored
Merge pull request #469 from arunvenmany-ibm/variable_include_issue_fix
variable resolution added for include in install feature
2 parents 4870015 + 978f4f9 commit 29c07e9

File tree

7 files changed

+170
-65
lines changed

7 files changed

+170
-65
lines changed

src/main/java/io/openliberty/tools/common/plugins/config/ServerConfigDocument.java

Lines changed: 11 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.Arrays;
3030
import java.util.HashMap;
3131
import java.util.HashSet;
32+
import java.util.List;
3233
import java.util.Optional;
3334
import java.util.Set;
3435
import java.util.Map;
@@ -58,6 +59,8 @@
5859
import io.openliberty.tools.common.plugins.util.ServerFeatureUtil;
5960
import io.openliberty.tools.common.plugins.util.VariableUtility;
6061

62+
import static io.openliberty.tools.common.plugins.util.VariableUtility.parseVariables;
63+
6164
// Moved from ci.maven/liberty-maven-plugin/src/main/java/net/wasdev/wlp/maven/plugins/ServerConfigDocument.java
6265
public class ServerConfigDocument {
6366

@@ -83,7 +86,7 @@ public class ServerConfigDocument {
8386
private static final XPathExpression XPATH_SERVER_SPRINGBOOT_APPLICATION;
8487
private static final XPathExpression XPATH_SERVER_ENTERPRISE_APPLICATION;
8588
private static final XPathExpression XPATH_SERVER_INCLUDE;
86-
private static final XPathExpression XPATH_SERVER_VARIABLE;
89+
public static final XPathExpression XPATH_SERVER_VARIABLE;
8790
private static final XPathExpression XPATH_ALL_SERVER_APPLICATIONS;
8891

8992

@@ -775,49 +778,19 @@ private boolean isValidURL(String url) {
775778

776779

777780
public void parseVariablesForDefaultValues(Document doc) throws XPathExpressionException {
778-
parseVariables(doc, true, false, false);
781+
List<Properties> propsList = parseVariables(doc, true, false, false);
782+
defaultProps.putAll(propsList.get(1));
779783
}
780784

781785
private void parseVariablesForValues(Document doc) throws XPathExpressionException {
782-
parseVariables(doc, false, true, false);
786+
List<Properties> propsList = parseVariables(doc, false, true, false);
787+
props.putAll(propsList.get(0));
783788
}
784789

785790
public void parseVariablesForBothValues(Document doc) throws XPathExpressionException {
786-
parseVariables(doc, false, false, true);
787-
}
788-
789-
private void parseVariables(Document doc, boolean defaultValues, boolean values, boolean both) throws XPathExpressionException {
790-
// parse input document
791-
NodeList nodeList = (NodeList) XPATH_SERVER_VARIABLE.evaluate(doc, XPathConstants.NODESET);
792-
793-
for (int i = 0; i < nodeList.getLength(); i++) {
794-
NamedNodeMap attr = nodeList.item(i).getAttributes();
795-
796-
String varName = attr.getNamedItem("name").getNodeValue();
797-
798-
if (!varName.isEmpty()) {
799-
// A variable can have either a value attribute OR a defaultValue attribute.
800-
String varValue = getValue(attr, "value");
801-
String varDefaultValue = getValue(attr, "defaultValue");
802-
803-
if ((values || both) && (varValue != null && !varValue.isEmpty())) {
804-
props.setProperty(varName, varValue);
805-
}
806-
807-
if ((defaultValues || both) && (varDefaultValue != null && ! varDefaultValue.isEmpty())) {
808-
defaultProps.setProperty(varName, varDefaultValue);
809-
}
810-
}
811-
}
812-
}
813-
814-
private String getValue(NamedNodeMap attr, String nodeName) {
815-
String value = null;
816-
Node valueNode = attr.getNamedItem(nodeName);
817-
if (valueNode != null) {
818-
value = valueNode.getNodeValue();
819-
}
820-
return value;
791+
List<Properties> propsList = parseVariables(doc, false, false, true);
792+
props.putAll(propsList.get(0));
793+
defaultProps.putAll(propsList.get(1));
821794
}
822795

823796
public void parseIncludeVariables(Document doc) throws XPathExpressionException, IOException, SAXException {

src/main/java/io/openliberty/tools/common/plugins/util/ServerFeatureUtil.java

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* (C) Copyright IBM Corporation 2019, 2024.
2+
* (C) Copyright IBM Corporation 2019, 2025.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -44,6 +44,7 @@
4444
import javax.xml.parsers.DocumentBuilder;
4545
import javax.xml.parsers.DocumentBuilderFactory;
4646
import javax.xml.parsers.ParserConfigurationException;
47+
import javax.xml.xpath.XPathExpressionException;
4748

4849
import org.apache.commons.io.FileUtils;
4950
import org.apache.commons.io.comparator.NameFileComparator;
@@ -218,7 +219,7 @@ public FeaturesPlatforms getServerFeatures(File serverDirectory, File serverXmlF
218219
} else if (libertyDirectoryPropertyToFile == null) {
219220
warn("The properties for directories are null and could lead to server include files not being processed for server features.");
220221
}
221-
Properties bootstrapProperties = getBootstrapProperties(new File(serverDirectory, "bootstrap.properties"));
222+
Properties bootstrapProperties = getPropertiesFromFile(new File(serverDirectory, "bootstrap.properties"));
222223
FeaturesPlatforms result = getConfigDropinsFeatures(null, serverDirectory, bootstrapProperties, "defaults", dropinsFilesToIgnore);
223224
if (serverXmlFile == null) {
224225
serverXmlFile = new File(serverDirectory, "server.xml");
@@ -412,7 +413,7 @@ public void error(SAXParseException e) throws SAXException {
412413
result.getFeatures().addAll(featuresToInstall);
413414
result.getPlatforms().addAll(platformsToInstall);
414415
} else if ("include".equals(child.getNodeName())){
415-
result = parseIncludeNode(result, serverDirectory, canonicalServerFile, bootstrapProperties, child, updatedParsedXmls);
416+
result = parseIncludeNode(result, serverDirectory, canonicalServerFile, bootstrapProperties, child, updatedParsedXmls, doc);
416417
}
417418
}
418419
}
@@ -491,29 +492,41 @@ private FeaturesPlatforms parseFeatureManagerNode(Element node) {
491492

492493
/**
493494
* Parse features from an include node.
494-
*
495-
* @param result2
496-
* The features that have been parsed so far.
497-
* @param serverDirectory
498-
* The server directory containing the server.xml.
499-
* @param serverFile
500-
* The parent server XML file containing the include node.
501-
* @param node
502-
* The include node.
503-
* @param updatedParsedXmls
504-
* The list of XML files that have been parsed so far.
495+
*
496+
* @param origResult The features that have been parsed so far.
497+
* @param serverDirectory The server directory containing the server.xml.
498+
* @param serverFile The parent server XML file containing the include node.
499+
* @param node The include node.
500+
* @param updatedParsedXmls The list of XML files that have been parsed so far.
501+
* @param doc XML Documemy
505502
* @return The set of features to install, or empty set if the cumulatively
506-
* parsed xml files only have featureManager sections but no
507-
* features to install, or null if there are no valid xml files or
508-
* they have no featureManager section
503+
* parsed xml files only have featureManager sections but no
504+
* features to install, or null if there are no valid xml files or
505+
* they have no featureManager section
509506
* @throws IOException
510507
*/
511508
private FeaturesPlatforms parseIncludeNode(FeaturesPlatforms origResult, File serverDirectory, File serverFile, Properties bootstrapProperties, Element node,
512-
List<File> updatedParsedXmls) {
509+
List<File> updatedParsedXmls, Document doc) {
513510
FeaturesPlatforms result = origResult;
514511
// Need to handle more variable substitution for include location.
512+
// currently we are only checking for server.xml, bootstrap.properties and server.env
515513
String nodeValue = node.getAttribute("location");
516-
String includeFileName = VariableUtility.resolveVariables(this, nodeValue, null, bootstrapProperties, new Properties(), getLibertyDirectoryPropertyFiles());
514+
Properties props = new Properties();
515+
Properties serverEnvProps = getPropertiesFromFile(new File(serverDirectory, "server.env"));
516+
props.putAll(serverEnvProps);
517+
props.putAll(bootstrapProperties);
518+
Properties defaultProps = new Properties();
519+
520+
try {
521+
List<Properties> resultMap = VariableUtility.parseVariables(doc, true, true, true);
522+
props.putAll(resultMap.get(0));
523+
defaultProps.putAll(resultMap.get(1));
524+
} catch (XPathExpressionException e) {
525+
warn("The server file " + serverFile + " cannot be parsed. Skipping the included features variable resolution for this file");
526+
debug("Exception received: " + e.getMessage(), e);
527+
}
528+
529+
String includeFileName = VariableUtility.resolveVariables(this, nodeValue, null, props, defaultProps, getLibertyDirectoryPropertyFiles());
517530

518531
if (includeFileName == null || includeFileName.trim().isEmpty()) {
519532
warn("Unable to parse include file "+nodeValue+". Skipping the included features.");
@@ -623,23 +636,28 @@ private FeaturesPlatforms handleOnConflict(FeaturesPlatforms origResult, String
623636
return result;
624637
}
625638

626-
private Properties getBootstrapProperties(File bootstrapProperties) {
639+
/**
640+
*
641+
* @param propertiesFile properties file
642+
* @return
643+
*/
644+
private Properties getPropertiesFromFile(File propertiesFile) {
627645
Properties prop = new Properties();
628-
if (bootstrapProperties != null && bootstrapProperties.exists()) {
646+
if (propertiesFile != null && propertiesFile.exists()) {
629647
FileInputStream stream = null;
630648
try {
631-
stream = new FileInputStream(bootstrapProperties);
649+
stream = new FileInputStream(propertiesFile);
632650
prop.load(stream);
633651
} catch (IOException e) {
634-
warn("The bootstrap.properties file " + bootstrapProperties.getAbsolutePath()
635-
+ " could not be loaded. Skipping the bootstrap.properties file.");
652+
warn("Properties from the file " + propertiesFile.getAbsolutePath()
653+
+ " could not be loaded. Skipping the properties file.");
636654
debug("Exception received: "+e.getMessage(), e);
637655
} finally {
638656
if (stream != null) {
639657
try {
640658
stream.close();
641659
} catch (IOException e) {
642-
debug("Could not close input stream for file " + bootstrapProperties.getAbsolutePath(), e);
660+
debug("Could not close input stream for file " + propertiesFile.getAbsolutePath(), e);
643661
}
644662
}
645663
}

src/main/java/io/openliberty/tools/common/plugins/util/VariableUtility.java

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* (C) Copyright IBM Corporation 2023, 2424
2+
* (C) Copyright IBM Corporation 2023, 2025
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,14 +15,25 @@
1515
*/package io.openliberty.tools.common.plugins.util;
1616

1717
import java.io.File;
18+
import java.util.ArrayList;
1819
import java.util.Collection;
1920
import java.util.HashSet;
21+
import java.util.List;
2022
import java.util.Map;
2123
import java.util.Properties;
2224
import java.util.regex.Matcher;
2325
import java.util.regex.Pattern;
2426

2527
import io.openliberty.tools.common.CommonLoggerI;
28+
import org.w3c.dom.Document;
29+
import org.w3c.dom.NamedNodeMap;
30+
import org.w3c.dom.Node;
31+
import org.w3c.dom.NodeList;
32+
33+
import javax.xml.xpath.XPathConstants;
34+
import javax.xml.xpath.XPathExpressionException;
35+
36+
import static io.openliberty.tools.common.plugins.config.ServerConfigDocument.XPATH_SERVER_VARIABLE;
2637

2738
public class VariableUtility {
2839
private static final String VARIABLE_NAME_PATTERN = "\\$\\{(.*?)\\}";
@@ -146,4 +157,54 @@ private static String lookupProperty(Properties prop, Properties defaultProps, S
146157
}
147158
return null;
148159
}
160+
161+
/**
162+
* <p>Parse variables for an xml document</p>
163+
* @param doc xml doc
164+
* @param defaultValues populate only default values property object
165+
* @param values populate only actual values property object
166+
* @param both both values property object will be populated
167+
* @return list of properties, including actual and default values
168+
* @throws XPathExpressionException
169+
*/
170+
public static List<Properties> parseVariables(Document doc, boolean defaultValues, boolean values, boolean both) throws XPathExpressionException {
171+
// parse input document
172+
NodeList nodeList = (NodeList) XPATH_SERVER_VARIABLE.evaluate(doc, XPathConstants.NODESET);
173+
Properties props = new Properties();
174+
Properties defaultProps = new Properties();
175+
176+
for (int i = 0; i < nodeList.getLength(); i++) {
177+
NamedNodeMap attr = nodeList.item(i).getAttributes();
178+
179+
String varName = attr.getNamedItem("name").getNodeValue();
180+
181+
if (!varName.isEmpty()) {
182+
// A variable can have either a value attribute OR a defaultValue attribute.
183+
String varValue = getValue(attr, "value");
184+
String varDefaultValue = getValue(attr, "defaultValue");
185+
186+
if ((values || both) && (varValue != null && !varValue.isEmpty())) {
187+
props.setProperty(varName, varValue);
188+
}
189+
190+
if ((defaultValues || both) && (varDefaultValue != null && ! varDefaultValue.isEmpty())) {
191+
defaultProps.setProperty(varName, varDefaultValue);
192+
}
193+
}
194+
}
195+
List<Properties> result = new ArrayList<>();
196+
result.add(props);
197+
result.add(defaultProps);
198+
return result;
199+
}
200+
201+
private static String getValue(NamedNodeMap attr, String nodeName) {
202+
String value = null;
203+
Node valueNode = attr.getNamedItem(nodeName);
204+
if (valueNode != null) {
205+
value = valueNode.getNodeValue();
206+
}
207+
return value;
208+
}
209+
149210
}

src/test/java/io/openliberty/tools/common/plugins/util/InstallFeatureUtilGetServerFeaturesTest.java

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -759,5 +759,41 @@ public void testUserFeaturesImproper() throws Exception{
759759

760760
verifyServerFeatures(expected);
761761
}
762-
762+
763+
@Test
764+
public void testIncludeUrlFromVariableServerXMLDefaultValue() throws Exception{
765+
copyAsName("server_include_variable_default_value.xml", "server.xml");
766+
copy("extraFeatures.xml");
767+
768+
Set<String> expected = new HashSet<String>();
769+
expected.add("orig");
770+
expected.add("extra");
771+
772+
verifyServerFeatures(expected);
773+
}
774+
775+
@Test
776+
public void testIncludeUrlFromVariableServerXML() throws Exception{
777+
copyAsName("server_include_variable.xml", "server.xml");
778+
copy("extraFeatures.xml");
779+
780+
Set<String> expected = new HashSet<String>();
781+
expected.add("orig");
782+
expected.add("extra");
783+
784+
verifyServerFeatures(expected);
785+
}
786+
787+
@Test
788+
public void testIncludeUrlFromVariableServerEnv() throws Exception{
789+
replaceIncludeLocation(src.toURI().toURL() + "\\$\\{extras.filename\\}");
790+
copy("server.env");
791+
copy("extraFeatures.xml");
792+
793+
Set<String> expected = new HashSet<String>();
794+
expected.add("orig");
795+
expected.add("extra");
796+
797+
verifyServerFeatures(expected);
798+
}
763799
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
extras.filename=extraFeatures.xml
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<server description="new server">
3+
<featureManager>
4+
<feature>orig</feature>
5+
</featureManager>
6+
<variable name="featureLoc" value="extraFeatures.xml"/>
7+
<include location="${featureLoc}" onConflict="MERGE"/>
8+
</server>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<server description="new server">
3+
<featureManager>
4+
<feature>orig</feature>
5+
</featureManager>
6+
<variable name="featureLoc" defaultValue="extraFeatures.xml"/>
7+
<include location="${featureLoc}" onConflict="MERGE"/>
8+
</server>

0 commit comments

Comments
 (0)