Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support directory as valid location to include in server configuration #425

Merged
merged 8 commits into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,15 @@
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.StreamSupport;
import java.util.Map;
import java.util.Properties;

Expand Down Expand Up @@ -360,17 +366,18 @@ private static void parseInclude(Document doc) throws XPathExpressionException,

if (includeFileName == null || includeFileName.trim().isEmpty()) {
log.warn("Unable to resolve include file location "+nodeValue+". Skipping the included file during application location processing.");
return;
continue;
}

Document docIncl = getIncludeDoc(includeFileName);

if (docIncl != null) {
parseApplication(docIncl, XPATH_SERVER_APPLICATION);
parseApplication(docIncl, XPATH_SERVER_WEB_APPLICATION);
parseApplication(docIncl, XPATH_SERVER_ENTERPRISE_APPLICATION);
// handle nested include elements
parseInclude(docIncl);
ArrayList<Document> inclDocs = getIncludeDoc(includeFileName);
for (Document inclDoc : inclDocs) {
if (inclDoc != null) {
parseApplication(inclDoc, XPATH_SERVER_APPLICATION);
parseApplication(inclDoc, XPATH_SERVER_WEB_APPLICATION);
parseApplication(inclDoc, XPATH_SERVER_ENTERPRISE_APPLICATION);
// handle nested include elements
parseInclude(inclDoc);
}
}
}
}
Expand Down Expand Up @@ -422,8 +429,8 @@ private static void parseDropinsFile(File file) throws IOException, XPathExpress
}
}

private static Document getIncludeDoc(String loc) throws IOException, SAXException {

private static ArrayList<Document> getIncludeDoc(String loc) throws IOException, SAXException {
ArrayList<Document> docs = new ArrayList<Document>();
Document doc = null;
File locFile = null;

Expand All @@ -432,14 +439,12 @@ private static Document getIncludeDoc(String loc) throws IOException, SAXExcepti
URL url = new URL(loc);
URLConnection connection = url.openConnection();
doc = parseDocument(connection.getInputStream());
docs.add(doc);
}
} else if (loc.startsWith("file:")) {
if (isValidURL(loc)) {
locFile = new File(loc);
if (locFile.exists()) {
InputStream inputStream = new FileInputStream(locFile.getCanonicalPath());
doc = parseDocument(inputStream);
}
parseDocumentFromFile(locFile, docs);
cherylking marked this conversation as resolved.
Show resolved Hide resolved
}
} else if (loc.startsWith("ftp:")) {
// TODO handle ftp protocol
Expand All @@ -448,10 +453,7 @@ private static Document getIncludeDoc(String loc) throws IOException, SAXExcepti

// check if absolute file
if (locFile.isAbsolute()) {
if (locFile.exists()) {
InputStream inputStream = new FileInputStream(locFile.getCanonicalPath());
doc = parseDocument(inputStream);
}
parseDocumentFromFile(locFile, docs);
} else {
// check configDirectory first if exists
if (configDirectory != null && configDirectory.exists()) {
Expand All @@ -461,13 +463,58 @@ private static Document getIncludeDoc(String loc) throws IOException, SAXExcepti
if (locFile == null || !locFile.exists()) {
locFile = new File(getServerXML().getParentFile(), loc);
}

if (locFile != null && locFile.exists()) {
InputStream inputStream = new FileInputStream(locFile.getCanonicalPath());
doc = parseDocument(inputStream);
}
parseDocumentFromFile(locFile, docs);
}
}
return docs;
}

/**
* Parses file or directory for all xml documents.
* @param file
* @param docs
* @throws FileNotFoundException
* @throws IOException
* @throws SAXException
*/
private static void parseDocumentFromFile(File file, ArrayList<Document> docs) throws FileNotFoundException, IOException, SAXException {
Document doc = null;
if (file == null || !file.exists()) {
log.debug("Unable to parse from file: " + file.getCanonicalPath());
return;
cherylking marked this conversation as resolved.
Show resolved Hide resolved
}
if (file.isFile()) {
doc = parseDocument(file);
docs.add(doc);
}
if (file.isDirectory()) {
parseDocumentsInDirectory(file, docs);
}
}

/**
* In a given directory, parse all direct children xml files in alphabetical order by filename.
* @param directory
* @param docs
* @throws IOException
*/
private static void parseDocumentsInDirectory(File directory, ArrayList<Document> docs) throws IOException {
DirectoryStream<Path> dstream = Files.newDirectoryStream(directory.toPath(), "*.xml");
StreamSupport.stream(dstream.spliterator(), false)
.sorted(Comparator.comparing(Path::toString))
.forEach(p -> {
try {
docs.add(parseDocument(p.toFile()));
} catch (Exception e) {
e.printStackTrace();
evie-lau marked this conversation as resolved.
Show resolved Hide resolved
}
});
}

private static Document parseDocument(File file) throws FileNotFoundException, IOException, SAXException {
InputStream is = new FileInputStream(file.getCanonicalPath());
Document doc = parseDocument(is);
is.close();
evie-lau marked this conversation as resolved.
Show resolved Hide resolved
return doc;
}

Expand Down Expand Up @@ -559,17 +606,19 @@ private static void parseIncludeVariables(Document doc) throws XPathExpressionEx

if (includeFileName == null || includeFileName.trim().isEmpty()) {
log.warn("Unable to resolve include file location "+nodeValue+". Skipping the included file during application location processing.");
return;
continue;
}

Document docIncl = getIncludeDoc(includeFileName);

if (docIncl != null) {
parseVariablesForBothValues(docIncl);
// handle nested include elements
parseIncludeVariables(docIncl);
} else {
log.warn("Unable to parse include file "+includeFileName+". Skipping the included file during application location processing.");

ArrayList<Document> inclDocs = getIncludeDoc(includeFileName);

for (Document inclDoc : inclDocs) {
if (inclDoc != null) {
cherylking marked this conversation as resolved.
Show resolved Hide resolved
parseVariablesForBothValues(inclDoc);
// handle nested include elements
parseIncludeVariables(inclDoc);
} else {
log.warn("Unable to parse include file "+includeFileName+". Skipping the included file during application location processing.");
cherylking marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.net.URLClassLoader;
import java.security.AccessController;
import java.security.PrivilegedActionException;
Expand All @@ -38,6 +41,7 @@
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.stream.StreamSupport;
import java.util.logging.Level;

import javax.xml.parsers.DocumentBuilder;
Expand Down Expand Up @@ -412,9 +416,10 @@ private Set<String> parseFeatureManagerNode(Element node) {
* 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 Set<String> parseIncludeNode(Set<String> origResult, File serverDirectory, File serverFile, Properties bootstrapProperties, Element node,
List<File> updatedParsedXmls) {
List<File> updatedParsedXmls) throws IOException {
evie-lau marked this conversation as resolved.
Show resolved Hide resolved
Set<String> result = origResult;
// Need to handle more variable substitution for include location.
String nodeValue = node.getAttribute("location");
Expand Down Expand Up @@ -453,13 +458,32 @@ private Set<String> parseIncludeNode(Set<String> origResult, File serverDirector
debug("Exception received: "+e.getMessage(), e);
return result;
}
if (!updatedParsedXmls.contains(includeFile)) {
String onConflict = node.getAttribute("onConflict");
Set<String> features = getServerXmlFeatures(null, serverDirectory, includeFile, bootstrapProperties, updatedParsedXmls);
if (features != null && !features.isEmpty()) {
info("Features were included for file "+ includeFileName);

ArrayList<File> includeFiles = new ArrayList<File>();
if (includeFile.isDirectory()) {
DirectoryStream<Path> dstream = Files.newDirectoryStream(includeFile.toPath(), "*.xml");
StreamSupport.stream(dstream.spliterator(), false)
.sorted(Comparator.comparing(Path::toString))
.forEach(p -> {
try {
includeFiles.add(p.toFile());
} catch (Exception e) {
e.printStackTrace();
}
});
} else {
includeFiles.add(includeFile);
}

for (File file : includeFiles) {
if (!updatedParsedXmls.contains(file)) {
String onConflict = node.getAttribute("onConflict");
Set<String> features = getServerXmlFeatures(null, serverDirectory, file, bootstrapProperties, updatedParsedXmls);
if (features != null && !features.isEmpty()) {
info("Features were included for file "+ file.getCanonicalPath());
}
result = handleOnConflict(result, onConflict, features);
}
result = handleOnConflict(result, onConflict, features);
}
return result;
}
Expand Down Expand Up @@ -534,7 +558,7 @@ private String getRelativeServerFilePath(File serverDirectory, File serverFile)
} catch (IOException e1) {
debug("Unable to determine the file path of " + serverFile + " relative to the server directory "
+ serverDirectory);
return serverFile.toString();
return serverFile.toString();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,17 @@ private void copyAsName(String origName, String newName) throws IOException {

private void copy(String origName) throws IOException {
File file = new File(src, origName);
FileUtils.copyFileToDirectory(file, serverDirectory);
if (file.isFile()) {
FileUtils.copyFileToDirectory(file, serverDirectory);
} else {
FileUtils.copyDirectoryToDirectory(file, serverDirectory);
}
}

private void verifyServerFeatures(Set<String> expected) throws Exception {
Set<String> getServerResult = util.getServerFeatures(serverDirectory, null);
assertEquals("The features returned from getServerFeatures do not equal the expected features.", expected, getServerResult);
}


private void verifyServerFeatures(Set<String> expected, Set<String> ignoreFiles) throws Exception {
Set<String> getServerResult = util.getServerFeatures(serverDirectory, null, ignoreFiles);
assertEquals("The features returned from getServerFeatures do not equal the expected features.", expected, getServerResult);
Expand Down Expand Up @@ -589,6 +591,52 @@ private void replaceIncludeLocation(String includeLocation) throws Exception {
content = content.replaceAll("@includeReplacementToken@", includeReplacement);
Files.write(serverXmlPath, content.getBytes(charset));
}

/**
* Tests server.xml with include dir
* @throws Exception
*/
@Test
public void testIncludeDir() throws Exception {
replaceIncludeDir("includeDir");

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

verifyServerFeatures(expected);
}

@Test
public void testIncludeDirReplace() throws Exception {
copyAsName("server_dir_replace.xml", "server.xml");
copy("includeDir");

// only the last replace should be kept
Set<String> expected = new HashSet<String>();
expected.add("extra4");

verifyServerFeatures(expected);
}

@Test
public void testIncludeDirIgnore() throws Exception {
copyAsName("server_dir_ignore.xml", "server.xml");
copy("includeDir");

// only the last replace should be kept
Set<String> expected = new HashSet<String>();
expected.add("orig");

verifyServerFeatures(expected);
}

private void replaceIncludeDir(String includeDirName) throws Exception {
File includeDir = new File(src, includeDirName);
replaceIncludeLocation(includeDir.getCanonicalPath());
}

/**
* Tests server.xml with user features
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,23 @@ public void testAppLocationUsesLibertyProperty() throws Exception {
String compareAppLocation3 = "test-war-three.war";
// this next one won't resolve because the var referenced in the include location uses a variable with a non-default value in server.xml
String compareAppLocation4 = "${project.artifactId.four}.ear";
String compareAppLocation5 = "test-war-five.war";
String compareAppLocation6 = "test-war-six.war";

Map<String, File> libertyDirectoryPropertyToFile = getLibertyDirectoryPropertyFiles(log, serverDirectory);

File serverXML = new File(serverDirectory, "server.xml");

ServerConfigDocument scd = ServerConfigDocument.getInstance(log, serverXML, serverDirectory, null, null, null, true, libertyDirectoryPropertyToFile);
Set<String> locations = scd.getLocations();
assertTrue("Expected four app locations", locations.size() == 4);
assertTrue("Expected six app locations", locations.size() == 6);

boolean locOneFound = false;
boolean locTwoFound = false;
boolean locThreeFound = false;
boolean locFourFound = false;
boolean locFiveFound = false;
boolean locSixFound = false;

for (String loc : locations) {
if (loc.contains("-two")) {
Expand All @@ -70,6 +74,12 @@ public void testAppLocationUsesLibertyProperty() throws Exception {
} else if (loc.endsWith(".ear")) {
assertTrue("Unexpected app location found: "+loc, loc.equals(compareAppLocation4));
locFourFound = true;
} else if (loc.contains("-five")) {
assertTrue("Unexpected app location found: "+loc, loc.equals(compareAppLocation5));
locFiveFound = true;
} else if (loc.contains("-six")) {
assertTrue("Unexpected app location found: "+loc, loc.equals(compareAppLocation6));
locSixFound = true;
} else {
assertTrue("Unexpected app location found: "+loc, loc.equals(compareAppLocation1));
locOneFound = true;
Expand All @@ -80,6 +90,8 @@ public void testAppLocationUsesLibertyProperty() throws Exception {
assertTrue("App location two not found.", locTwoFound);
assertTrue("App location three not found.", locThreeFound);
assertTrue("App location four not found.", locFourFound);
assertTrue("App location five not found.", locFiveFound);
assertTrue("App location six not found.", locSixFound);

}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<server description="">
<variable name="project.artifactId.five" value="test-war-five"/>
</server>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<server description="">
<variable name="project.artifactId.six" value="test-war-six"/>
</server>
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@
<include location="${includeLocationNonDefault}/firstInclude.xml"/>
<webApplication location="${project.artifactId.four}.ear"/>

<include location="${server.config.dir}/includeDir"/>
<webApplication location="${project.artifactId.five}.war"/>
<webApplication location="${project.artifactId.six}.war"/>

</server>
Loading
Loading