Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion src/main/java/io/openliberty/tools/common/CommonLoggerI.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* (C) Copyright IBM Corporation 2019.
* (C) Copyright IBM Corporation 2019, 2026.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,6 +17,27 @@

public interface CommonLoggerI {

/**
Comment thread
cherylking marked this conversation as resolved.
* Returns a no-op {@code CommonLoggerI} that silently discards all log calls.
* Useful when a logger instance is required by an API but no output is desired.
*/
static CommonLoggerI noop() {
return NoopLogger.INSTANCE;
}

/** Singleton backing {@link #noop()}. */
final class NoopLogger implements CommonLoggerI {
static final NoopLogger INSTANCE = new NoopLogger();
private NoopLogger() {}
@Override public void debug(String msg) {}
@Override public void debug(String msg, Throwable e) {}
@Override public void debug(Throwable e) {}
@Override public void warn(String msg) {}
@Override public void info(String msg) {}
@Override public void error(String msg) {}
@Override public boolean isDebugEnabled() { return false; }
}

/**
* Log debug
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;

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.XPathExpression;
Expand All @@ -53,6 +49,7 @@
import org.apache.commons.io.comparator.NameFileComparator;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

Expand All @@ -75,17 +72,22 @@ public class ServerConfigDocument {
private Set<String> namelessLocations;
private Set<String> locations;
private HashMap<String, String> locationsAndNames;
private Map<String, String> httpEndpointAttributes;
private Properties props;
private Properties defaultProps;
private Map<String, File> libertyDirectoryPropertyToFile = null;

Optional<String> springBootAppNodeLocation = Optional.empty();
Optional<String> springBootAppNodeDocumentURI = Optional.empty();

public static final String HTTP_PORT_ATTR = "httpPort";
public static final String HTTPS_PORT_ATTR = "httpsPort";
Comment thread
cherylking marked this conversation as resolved.

private static final XPathExpression XPATH_SERVER_APPLICATION;
private static final XPathExpression XPATH_SERVER_WEB_APPLICATION;
private static final XPathExpression XPATH_SERVER_SPRINGBOOT_APPLICATION;
private static final XPathExpression XPATH_SERVER_ENTERPRISE_APPLICATION;
private static final XPathExpression XPATH_SERVER_HTTP_ENDPOINT;
private static final XPathExpression XPATH_SERVER_INCLUDE;
public static final XPathExpression XPATH_SERVER_VARIABLE;
private static final XPathExpression XPATH_ALL_SERVER_APPLICATIONS;
Expand All @@ -102,6 +104,7 @@ public class ServerConfigDocument {
XPATH_SERVER_WEB_APPLICATION = xPath.compile("/server/webApplication");
XPATH_SERVER_SPRINGBOOT_APPLICATION = xPath.compile("/server/springBootApplication");
XPATH_SERVER_ENTERPRISE_APPLICATION = xPath.compile("/server/enterpriseApplication");
XPATH_SERVER_HTTP_ENDPOINT = xPath.compile("/server/httpEndpoint");
XPATH_SERVER_INCLUDE = xPath.compile("/server/include");
XPATH_SERVER_VARIABLE = xPath.compile("/server/variable");
XPATH_ALL_SERVER_APPLICATIONS = xPath.compile("/server/application | /server/webApplication | /server/enterpriseApplication | /server/springBootApplication");
Expand All @@ -127,6 +130,10 @@ public Set<String> getNamelessLocations() {
return namelessLocations;
}

public Map<String, String> getHttpEndpointAttributes() {
return httpEndpointAttributes;
}

public Properties getProperties() {
return props;
}
Expand Down Expand Up @@ -165,6 +172,7 @@ public ServerConfigDocument(CommonLoggerI log, File originalServerXMLFile, Map<S
names = new HashSet<String>();
namelessLocations = new HashSet<String>();
locationsAndNames = new HashMap<String, String>();
httpEndpointAttributes = new HashMap<String, String>();
props = new Properties();
defaultProps = new Properties();
this.originalServerXMLFile = originalServerXMLFile;
Expand Down Expand Up @@ -196,38 +204,13 @@ public ServerConfigDocument(CommonLoggerI log, File originalServerXMLFile, Map<S
names = new HashSet<String>();
namelessLocations = new HashSet<String>();
locationsAndNames = new HashMap<String, String>();
httpEndpointAttributes = new HashMap<String, String>();
props = new Properties();
if (initProperties != null) props.putAll(initProperties);
defaultProps = new Properties();
this.originalServerXMLFile = originalServerXMLFile;
}

private DocumentBuilder getDocumentBuilder() {
DocumentBuilder docBuilder;

DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
docBuilderFactory.setIgnoringComments(true);
docBuilderFactory.setCoalescing(true);
docBuilderFactory.setIgnoringElementContentWhitespace(true);
docBuilderFactory.setValidating(false);
try {
docBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
docBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
docBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
docBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
docBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
docBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
docBuilderFactory.setXIncludeAware(false);
docBuilderFactory.setExpandEntityReferences(false);
docBuilder = docBuilderFactory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
// fail catastrophically if we can't create a document builder
throw new RuntimeException(e);
}

return docBuilder;
}

/**
// Server variable precedence in ascending order if defined in multiple locations.
// 1. variable default values in the server.xml file
Expand Down Expand Up @@ -290,6 +273,7 @@ public void initializeAppsLocation() throws PluginExecutionException {
parseApplication(doc, XPATH_SERVER_ENTERPRISE_APPLICATION);
parseApplication(doc, XPATH_SERVER_SPRINGBOOT_APPLICATION);
parseNames(doc, XPATH_ALL_SERVER_APPLICATIONS);
parseHttpEndpoint(doc);
parseInclude(doc);
parseConfigDropinsDir();

Expand Down Expand Up @@ -582,6 +566,29 @@ private void parseNames(Document doc, XPathExpression expression) throws XPathEx
}
}

private void parseHttpEndpoint(Document doc) throws XPathExpressionException {
if (doc == null) {
return;
}
NodeList nodeList = (NodeList) XPATH_SERVER_HTTP_ENDPOINT.evaluate(doc, XPathConstants.NODESET);
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
if (node instanceof Element) {
Element elem = (Element) node;

String httpAttribute = elem.getAttribute(HTTP_PORT_ATTR);
if (!httpAttribute.isEmpty()) {
httpEndpointAttributes.put(HTTP_PORT_ATTR, httpAttribute);
}

String httpsAttribute = elem.getAttribute(HTTPS_PORT_ATTR);
if (!httpsAttribute.isEmpty()) {
httpEndpointAttributes.put(HTTPS_PORT_ATTR, httpsAttribute);
}
}
}
}

public String findNameForLocation(String location) {
String appName = locationsAndNames.get(location);

Expand Down Expand Up @@ -656,6 +663,7 @@ private void parseInclude(Document doc) throws XPathExpressionException, IOExcep
parseApplication(inclDoc, XPATH_SERVER_SPRINGBOOT_APPLICATION);
parseApplication(inclDoc, XPATH_SERVER_ENTERPRISE_APPLICATION);
parseNames(inclDoc, XPATH_ALL_SERVER_APPLICATIONS);
parseHttpEndpoint(inclDoc);
// handle nested include elements
parseInclude(inclDoc);
}
Expand Down Expand Up @@ -699,6 +707,7 @@ private void parseDropinsFile(File file) throws IOException, XPathExpressionExce
parseApplication(doc, XPATH_SERVER_SPRINGBOOT_APPLICATION);
parseApplication(doc, XPATH_SERVER_ENTERPRISE_APPLICATION);
parseNames(doc, XPATH_ALL_SERVER_APPLICATIONS);
parseHttpEndpoint(doc);
parseInclude(doc);
}
}
Expand Down Expand Up @@ -809,10 +818,8 @@ private void parseDocumentsInDirectory(File directory, ArrayList<Document> docs)
* @throws SAXException
*/
public Document parseDocument(File file) throws FileNotFoundException, IOException {
try (FileInputStream is = new FileInputStream(file)) {
Document document= parseDocument(is);
document.setDocumentURI(file.getCanonicalPath());
return document;
try {
return XmlDocument.parseDocument(file);
} catch (SAXException ex) {
// If the file was not valid XML, assume it was some other non XML
// file in dropins.
Expand All @@ -824,13 +831,7 @@ public Document parseDocument(File file) throws FileNotFoundException, IOExcepti
private Document parseDocument(URL url) throws IOException, SAXException {
URLConnection connection = url.openConnection();
try (InputStream is = connection.getInputStream()) {
return parseDocument(is);
}
}

private Document parseDocument(InputStream in) throws SAXException, IOException {
try (InputStream ins = in) { // ins will be auto-closed
return getDocumentBuilder().parse(ins);
return XmlDocument.parseDocument(is);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* (C) Copyright IBM Corporation 2017, 2024.
* (C) Copyright IBM Corporation 2017, 2026.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -18,6 +18,7 @@
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
Expand All @@ -38,6 +39,7 @@
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
Comment thread
cherylking marked this conversation as resolved.
import org.w3c.dom.Text;
import org.xml.sax.SAXException;

Expand All @@ -46,29 +48,15 @@ public abstract class XmlDocument {
protected Document doc;

public void createDocument(String rootElement) throws ParserConfigurationException {
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
DocumentBuilder docBuilder = getDocumentBuilder();
doc = docBuilder.newDocument();
doc.setXmlStandalone(true);
Element element = doc.createElement(rootElement);
doc.appendChild(element);
}

public void createDocument(File xmlFile) throws ParserConfigurationException, SAXException, IOException {
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
builderFactory.setCoalescing(true);
builderFactory.setIgnoringElementContentWhitespace(true);
builderFactory.setValidating(false);
builderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
builderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
builderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
builderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
builderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
builderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
builderFactory.setXIncludeAware(false);
builderFactory.setExpandEntityReferences(false);
DocumentBuilder builder = builderFactory.newDocumentBuilder();
doc = builder.parse(xmlFile);
doc = parseDocument(xmlFile);
}

public void writeXMLDocument(String fileName) throws IOException, TransformerException {
Expand Down Expand Up @@ -112,6 +100,77 @@ protected boolean isWhitespace(Node node) {
return node != null && node instanceof Text && ((Text)node).getData().trim().isEmpty();
}

/**
* Creates and returns a securely configured {@link DocumentBuilder}.
*/
public static DocumentBuilder getDocumentBuilder() {
DocumentBuilder docBuilder;
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
docBuilderFactory.setIgnoringComments(true);
docBuilderFactory.setCoalescing(true);
docBuilderFactory.setIgnoringElementContentWhitespace(true);
docBuilderFactory.setValidating(false);
try {
docBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
docBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
docBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
docBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
docBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
docBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
docBuilderFactory.setXIncludeAware(false);
docBuilderFactory.setExpandEntityReferences(false);
docBuilder = docBuilderFactory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
// fail if we can't create a document builder
throw new RuntimeException(e);
}
return docBuilder;
}

public static Document parseDocument(File file) throws IOException, SAXException {
try (InputStream is = Files.newInputStream(file.toPath())) {
Document document = parseDocument(is);
document.setDocumentURI(file.getCanonicalPath());
return document;
}
}

public static Document parseDocument(InputStream in) throws SAXException, IOException {
try (InputStream ins = in) {
return getDocumentBuilder().parse(ins);
}
}

/**
* Returns the text content of the first element matching {@code tagName} in an XML file,
* or {@code null} if the file is absent, the tag is missing, or any parse error occurs.
*/
public static String readTextElementFromXmlFile(File xmlFile, String tagName) {
if (xmlFile == null || !xmlFile.isFile()) {
return null;
}
try {
Document doc = parseDocument(xmlFile);
NodeList nodes = doc.getElementsByTagName(tagName);
if (nodes.getLength() == 0) {
return null;
}
String text = nodes.item(0).getTextContent();
return (text != null && !text.trim().isEmpty()) ? text.trim() : null;
} catch (Exception e) {
return null;
}
}

/**
* Returns a {@link File} for the text content of the first element matching {@code tagName}
* in an XML file, or {@code null} if the file is absent, the tag is missing, or any parse error occurs.
*/
public static File getFileElementFromXmlFile(File xmlFile, String tagName) {
String path = readTextElementFromXmlFile(xmlFile, tagName);
return (path != null) ? new File(path) : null;
}

public static void addNewlineBeforeFirstElement(File f) throws IOException {
// look for "<?xml version="1.0" ... ?><server .../>" and add a newline
byte[] contents = Files.readAllBytes(f.toPath());
Expand Down
Loading
Loading