Skip to content

Commit

Permalink
variable resolution added for include in install feature
Browse files Browse the repository at this point in the history
Signed-off-by: Arun Venmany <[email protected]>
  • Loading branch information
arunvenmany-ibm committed Jan 23, 2025
1 parent 4870015 commit 7587a35
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public class ServerConfigDocument {
private static final XPathExpression XPATH_SERVER_SPRINGBOOT_APPLICATION;
private static final XPathExpression XPATH_SERVER_ENTERPRISE_APPLICATION;
private static final XPathExpression XPATH_SERVER_INCLUDE;
private static final XPathExpression XPATH_SERVER_VARIABLE;
public static final XPathExpression XPATH_SERVER_VARIABLE;
private static final XPathExpression XPATH_ALL_SERVER_APPLICATIONS;


Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* (C) Copyright IBM Corporation 2019, 2024.
* (C) Copyright IBM Corporation 2019, 2025.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -44,6 +44,7 @@
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathExpressionException;

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

/**
* Parse features from an include node.
*
* @param result2
* The features that have been parsed so far.
* @param serverDirectory
* The server directory containing the server.xml.
* @param serverFile
* The parent server XML file containing the include node.
* @param node
* The include node.
* @param updatedParsedXmls
* The list of XML files that have been parsed so far.
*
* @param origResult The features that have been parsed so far.
* @param serverDirectory The server directory containing the server.xml.
* @param serverFile The parent server XML file containing the include node.
* @param node The include node.
* @param updatedParsedXmls The list of XML files that have been parsed so far.
* @param doc XML Documemy
* @return The set of features to install, or empty set if the cumulatively
* parsed xml files only have featureManager sections but no
* features to install, or null if there are no valid xml files or
* they have no featureManager section
* parsed xml files only have featureManager sections but no
* features to install, or null if there are no valid xml files or
* they have no featureManager section
* @throws IOException
*/
private FeaturesPlatforms parseIncludeNode(FeaturesPlatforms origResult, File serverDirectory, File serverFile, Properties bootstrapProperties, Element node,
List<File> updatedParsedXmls) {
List<File> updatedParsedXmls, Document doc) {
FeaturesPlatforms result = origResult;
// Need to handle more variable substitution for include location.
// currently we are only checking for server.xml, bootstrap.properties and server.env
String nodeValue = node.getAttribute("location");
String includeFileName = VariableUtility.resolveVariables(this, nodeValue, null, bootstrapProperties, new Properties(), getLibertyDirectoryPropertyFiles());
Properties props = new Properties();
props.putAll(bootstrapProperties);
Properties serverEnvProps = getPropertiesFromFile(new File(serverDirectory, "server.env"));
props.putAll(serverEnvProps);
Properties defaultProps = new Properties();

try {
List<Properties> resultMap = VariableUtility.parseVariablesForXml(doc, true, true, true);
props.putAll(resultMap.get(0));
defaultProps.putAll(resultMap.get(1));
} catch (XPathExpressionException e) {
warn("The server file " + serverFile + " cannot be parsed. Skipping the included features variable resolution for this file");
debug("Exception received: " + e.getMessage(), e);
}

String includeFileName = VariableUtility.resolveVariables(this, nodeValue, null, props, defaultProps, getLibertyDirectoryPropertyFiles());

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

private Properties getBootstrapProperties(File bootstrapProperties) {
/**
*
* @param propertiesFile properties file
* @return
*/
private Properties getPropertiesFromFile(File propertiesFile) {
Properties prop = new Properties();
if (bootstrapProperties != null && bootstrapProperties.exists()) {
if (propertiesFile != null && propertiesFile.exists()) {
FileInputStream stream = null;
try {
stream = new FileInputStream(bootstrapProperties);
stream = new FileInputStream(propertiesFile);
prop.load(stream);
} catch (IOException e) {
warn("The bootstrap.properties file " + bootstrapProperties.getAbsolutePath()
+ " could not be loaded. Skipping the bootstrap.properties file.");
warn("Properties from the file " + propertiesFile.getAbsolutePath()
+ " could not be loaded. Skipping the properties file.");
debug("Exception received: "+e.getMessage(), e);
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
debug("Could not close input stream for file " + bootstrapProperties.getAbsolutePath(), e);
debug("Could not close input stream for file " + propertiesFile.getAbsolutePath(), e);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* (C) Copyright IBM Corporation 2023, 2424
* (C) Copyright IBM Corporation 2023, 2025
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,14 +15,25 @@
*/package io.openliberty.tools.common.plugins.util;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import io.openliberty.tools.common.CommonLoggerI;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;

import static io.openliberty.tools.common.plugins.config.ServerConfigDocument.XPATH_SERVER_VARIABLE;

public class VariableUtility {
private static final String VARIABLE_NAME_PATTERN = "\\$\\{(.*?)\\}";
Expand Down Expand Up @@ -146,4 +157,45 @@ private static String lookupProperty(Properties prop, Properties defaultProps, S
}
return null;
}

public static List<Properties> parseVariablesForXml(Document doc, boolean defaultValues, boolean values, boolean both) throws XPathExpressionException {
// parse input document
NodeList nodeList = (NodeList) XPATH_SERVER_VARIABLE.evaluate(doc, XPathConstants.NODESET);
Properties props = new Properties();
Properties defaultProps=new Properties();

for (int i = 0; i < nodeList.getLength(); i++) {
NamedNodeMap attr = nodeList.item(i).getAttributes();

String varName = attr.getNamedItem("name").getNodeValue();

if (!varName.isEmpty()) {
// A variable can have either a value attribute OR a defaultValue attribute.
String varValue = getValue(attr, "value");
String varDefaultValue = getValue(attr, "defaultValue");

if ((values || both) && (varValue != null && !varValue.isEmpty())) {
props.setProperty(varName, varValue);
}

if ((defaultValues || both) && (varDefaultValue != null && ! varDefaultValue.isEmpty())) {
defaultProps.setProperty(varName, varDefaultValue);
}
}
}
List<Properties>result=new ArrayList<>();
result.add(props);
result.add(defaultProps);
return result;
}

public static String getValue(NamedNodeMap attr, String nodeName) {
String value = null;
Node valueNode = attr.getNamedItem(nodeName);
if (valueNode != null) {
value = valueNode.getNodeValue();
}
return value;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -759,5 +759,41 @@ public void testUserFeaturesImproper() throws Exception{

verifyServerFeatures(expected);
}


@Test
public void testIncludeUrlFromVariableServerXMLDefaultValue() throws Exception{
copyAsName("server_include_variable_default_value.xml", "server.xml");
copy("extraFeatures.xml");

Set<String> expected = new HashSet<String>();
expected.add("orig");
expected.add("extra");

verifyServerFeatures(expected);
}

@Test
public void testIncludeUrlFromVariableServerXML() throws Exception{
copyAsName("server_include_variable.xml", "server.xml");
copy("extraFeatures.xml");

Set<String> expected = new HashSet<String>();
expected.add("orig");
expected.add("extra");

verifyServerFeatures(expected);
}

@Test
public void testIncludeUrlFromVariableServerEnv() throws Exception{
replaceIncludeLocation(src.toURI().toURL() + "\\$\\{extras.filename\\}");
copy("server.env");
copy("extraFeatures.xml");

Set<String> expected = new HashSet<String>();
expected.add("orig");
expected.add("extra");

verifyServerFeatures(expected);
}
}
1 change: 1 addition & 0 deletions src/test/resources/servers/server.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
extras.filename=extraFeatures.xml
8 changes: 8 additions & 0 deletions src/test/resources/servers/server_include_variable.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<server description="new server">
<featureManager>
<feature>orig</feature>
</featureManager>
<variable name="featureLoc" value="extraFeatures.xml"/>
<include location="${featureLoc}" onConflict="MERGE"/>
</server>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<server description="new server">
<featureManager>
<feature>orig</feature>
</featureManager>
<variable name="featureLoc" defaultValue="extraFeatures.xml"/>
<include location="${featureLoc}" onConflict="MERGE"/>
</server>

0 comments on commit 7587a35

Please sign in to comment.