Skip to content

Commit

Permalink
update GroovyXmlTransform to understand new first-level XML elements
Browse files Browse the repository at this point in the history
WildFly added new elements to the set of possible first-level elements
in the XML configuration files. This commit updates the Subtree class
to understand them (which requires making a difference between host.xml
and domain.xml, as host.xml is suddenly very similar to standalone.xml).

Also, to maintain correct order of the XML elements, it is no longer
sufficient to maintain a single list which blends all the possibilities,
so the list was split into three separate lists -- for the "domain" root
element, for "host" and for "server".
  • Loading branch information
Ladicek committed Feb 22, 2016
1 parent 9e67cb1 commit fd9d09b
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 39 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- if `OnlineOptions.forHost` wasn't called and `OnlineOptions.defaultHost`
is therefore `null`, operations against addresses `/core-service=...`
are now performed as-is instead of throwing an exception
- updated `GroovyXmlTransform` to understand new XML elements added in WildFly

## 0.9.5

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,68 @@ public String apply(Node input) {
}
};

private static final Ordering<Node> ORDERING = Ordering.explicit(
// this is a blend of "domain", "host" and "server" schemas that satisfies all ordering constraints
// see files [jboss-as|wildfly]-config_*.xsd

private static final Ordering<Node> DOMAIN_ORDERING = Ordering.explicit(
"extensions",
"system-properties",
"paths",
"vault",
"management",
"profile",
"domain-controller",
"profiles",
"interfaces",
"socket-binding-groups",
"socket-binding-group",
"deployments",
"deployment-overlays",
"server-groups",
"management-client-content"
).onResultOf(GET_NODE_NAME);

private static final Ordering<Node> HOST_ORDERING = Ordering.explicit(
"extensions",
"system-properties",
"paths",
"vault",
"management",
"domain-controller",
"interfaces",
"jvms",
"servers",
"management-client-content"
"profile",
"socket-binding-group"
).onResultOf(GET_NODE_NAME);

private static final Ordering<Node> SERVER_ORDERING = Ordering.explicit(
"extensions",
"system-properties",
"paths",
"vault",
"management",
"profile",
"interfaces",
"socket-binding-group",
"deployments",
"deployment-overlays"
).onResultOf(GET_NODE_NAME);

static String fix(String xml) {
try {
Node root = new XmlParser(false, false).parseText(xml);
Collections.sort((List<Node>) root.children(), ORDERING);

Ordering<Node> ordering;
if ("domain".equals(root.name())) {
ordering = DOMAIN_ORDERING;
} else if ("host".equals(root.name())) {
ordering = HOST_ORDERING;
} else if ("server".equals(root.name())) {
ordering = SERVER_ORDERING;
} else {
throw new IllegalArgumentException("Unknown root element '" + root.name() + "'");
}

Collections.sort((List<Node>) root.children(), ordering);
return XmlUtil.serialize(root);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,12 @@ public static Subtree management() {
return new Subtree(StaticSubtreeLocator.MANAGEMENT, SubtreeCreator.MANAGEMENT);
}

// using default profile in domain
// using default profile in domain.xml
public static Subtree profile() {
return new Subtree(ProfileSubtreeLocator.INSTANCE, SubtreeCreator.PROFILE);
}

// using default profile in domain
// using default profile in domain.xml
public static Subtree subsystem(String subsystemName) {
// can't create subsystem, don't know the version
return new Subtree(new SubsystemSubtreeLocator(subsystemName), null);
Expand All @@ -74,7 +74,7 @@ public static Subtree interfaces() {
return new Subtree(StaticSubtreeLocator.INTERFACES, SubtreeCreator.INTERFACES);
}

// in domain, tries to guess a corect name based on the default profile
// in domain.xml, tries to guess a corect name based on the default profile
public static Subtree socketBindingGroup() {
return new Subtree(SocketBindingGroupSubtreeLocator.INSTANCE, SubtreeCreator.SOCKET_BINDING_GROUP);
}
Expand Down Expand Up @@ -113,6 +113,37 @@ public static Subtree servers() {

// ---

private enum Type {
DOMAIN("domain.xml"),
HOST("host.xml"),
SERVER("standalone.xml"),
;

private final String description;

Type(String description) {
this.description = description;
}

@Override
public String toString() {
return description;
}

public static Type of(GPathResult root) {
String rootElement = root.name();
if ("domain".equals(rootElement)) {
return Type.DOMAIN;
} else if ("host".equals(rootElement)) {
return Type.HOST;
} else if ("server".equals(rootElement)) {
return Type.SERVER;
} else {
throw new IllegalArgumentException("Unknown root node '" + rootElement + "'");
}
}
}

private interface SubtreeLocator {
GPathResult locate(GPathResult root, OfflineOptions options) throws Exception;

Expand All @@ -125,33 +156,34 @@ public GPathResult locate(GPathResult root, OfflineOptions options) {
}

private static final class StaticSubtreeLocator implements SubtreeLocator {
static final SubtreeLocator EXTENSIONS = new StaticSubtreeLocator("extensions", false);
static final SubtreeLocator SYSTEM_PROPERTIES = new StaticSubtreeLocator("system-properties", false);
static final SubtreeLocator PATHS = new StaticSubtreeLocator("paths", false);
static final SubtreeLocator MANAGEMENT = new StaticSubtreeLocator("management", false);
static final SubtreeLocator INTERFACES = new StaticSubtreeLocator("interfaces", false);

static final SubtreeLocator PROFILES = new StaticSubtreeLocator("profiles", true);
static final SubtreeLocator SOCKET_BINDING_GROUPS = new StaticSubtreeLocator("socket-binding-groups", true);
static final SubtreeLocator SERVER_GROUPS = new StaticSubtreeLocator("server-groups", true);
static final SubtreeLocator DOMAIN_CONTROLLER = new StaticSubtreeLocator("domain-controller", true);
static final SubtreeLocator JVMS = new StaticSubtreeLocator("jvms", true);
static final SubtreeLocator SERVERS = new StaticSubtreeLocator("servers", true);
static final SubtreeLocator EXTENSIONS = new StaticSubtreeLocator("extensions", null);
static final SubtreeLocator SYSTEM_PROPERTIES = new StaticSubtreeLocator("system-properties", null);
static final SubtreeLocator PATHS = new StaticSubtreeLocator("paths", null);
static final SubtreeLocator MANAGEMENT = new StaticSubtreeLocator("management", null);
static final SubtreeLocator INTERFACES = new StaticSubtreeLocator("interfaces", null);

static final SubtreeLocator PROFILES = new StaticSubtreeLocator("profiles", Type.DOMAIN);
static final SubtreeLocator SOCKET_BINDING_GROUPS = new StaticSubtreeLocator("socket-binding-groups",
Type.DOMAIN);
static final SubtreeLocator SERVER_GROUPS = new StaticSubtreeLocator("server-groups", Type.DOMAIN);
static final SubtreeLocator DOMAIN_CONTROLLER = new StaticSubtreeLocator("domain-controller", Type.HOST);
static final SubtreeLocator JVMS = new StaticSubtreeLocator("jvms", Type.HOST);
static final SubtreeLocator SERVERS = new StaticSubtreeLocator("servers", Type.HOST);

private final String tagName; // only for a possible exception message
private final Class scriptClass;
private final boolean domainOnly;
private final Type onlyForType;

StaticSubtreeLocator(String tagName, boolean domainOnly) {
StaticSubtreeLocator(String tagName, Type onlyForType) {
String script = "root.\"" + tagName + "\"";
this.tagName = tagName;
this.scriptClass = (GroovyHolder.GROOVY.parseClass(script));
this.domainOnly = domainOnly;
this.scriptClass = GroovyHolder.GROOVY.parseClass(script);
this.onlyForType = onlyForType;
}

public GPathResult locate(GPathResult root, OfflineOptions options) throws Exception {
if (domainOnly && !options.isDomain) {
throw new IllegalArgumentException("Locating '" + tagName + "' is only possible in domain");
if (onlyForType != null && onlyForType != Type.of(root)) {
throw new IllegalArgumentException("Locating '" + tagName + "' is only possible in '" + onlyForType + "'");
}
Script script = (Script) scriptClass.newInstance();
script.setProperty("root", root);
Expand All @@ -162,24 +194,26 @@ public GPathResult locate(GPathResult root, OfflineOptions options) throws Excep
private static final class ProfileSubtreeLocator implements SubtreeLocator {
static final SubtreeLocator INSTANCE = new ProfileSubtreeLocator();

private static final Class STANDALONE_SCRIPT_CLASS = GroovyHolder.GROOVY.parseClass("root.profile");
private static final Class STANDALONE_OR_HOST_SCRIPT_CLASS = GroovyHolder.GROOVY.parseClass("root.profile");
private static final Class DOMAIN_SCRIPT_CLASS = GroovyHolder.GROOVY.parseClass("root.profiles.profile.find { it.@name == defaultProfile }");

@Override
public GPathResult locate(GPathResult root, OfflineOptions options) throws Exception {
boolean domain = Type.of(root) == Type.DOMAIN;

Script script = (Script) (
options.isDomain ? DOMAIN_SCRIPT_CLASS.newInstance() : STANDALONE_SCRIPT_CLASS.newInstance()
domain ? DOMAIN_SCRIPT_CLASS.newInstance() : STANDALONE_OR_HOST_SCRIPT_CLASS.newInstance()
);
script.setProperty("root", root);
if (options.isDomain) {
if (domain) {
script.setProperty("defaultProfile", options.defaultProfile);
}
return (GPathResult) script.run();
}
}

private static final class SubsystemSubtreeLocator implements SubtreeLocator {
private static final Class STANDALONE_SCRIPT_CLASS = GroovyHolder.GROOVY.parseClass("root.profile.subsystem.find { [email protected]().startsWith(\"urn:jboss:domain:${subsystemName}:\") }");
private static final Class STANDALONE_OR_HOST_SCRIPT_CLASS = GroovyHolder.GROOVY.parseClass("root.profile.subsystem.find { [email protected]().startsWith(\"urn:jboss:domain:${subsystemName}:\") }");
private static final Class DOMAIN_SCRIPT_CLASS = GroovyHolder.GROOVY.parseClass("root.profiles.profile.find { it.@name == defaultProfile }.subsystem.find { [email protected]().startsWith(\"urn:jboss:domain:${subsystemName}:\") }");

private final String subsystemName;
Expand All @@ -190,12 +224,14 @@ public SubsystemSubtreeLocator(String subsystemName) {

@Override
public GPathResult locate(GPathResult root, OfflineOptions options) throws Exception {
boolean domain = Type.of(root) == Type.DOMAIN;

Script script = (Script) (
options.isDomain ? DOMAIN_SCRIPT_CLASS.newInstance() : STANDALONE_SCRIPT_CLASS.newInstance()
domain ? DOMAIN_SCRIPT_CLASS.newInstance() : STANDALONE_OR_HOST_SCRIPT_CLASS.newInstance()
);
script.setProperty("root", root);
script.setProperty("subsystemName", subsystemName);
if (options.isDomain) {
if (domain) {
script.setProperty("defaultProfile", options.defaultProfile);
}
return (GPathResult) script.run();
Expand All @@ -205,16 +241,18 @@ public GPathResult locate(GPathResult root, OfflineOptions options) throws Excep
private static final class SocketBindingGroupSubtreeLocator implements SubtreeLocator {
static final SubtreeLocator INSTANCE = new SocketBindingGroupSubtreeLocator();

private static final Class STANDALONE_SCRIPT_CLASS = GroovyHolder.GROOVY.parseClass("root.\"socket-binding-group\"");
private static final Class STANDALONE_OR_HOST_SCRIPT_CLASS = GroovyHolder.GROOVY.parseClass("root.\"socket-binding-group\"");
private static final Class DOMAIN_SCRIPT_CLASS = GroovyHolder.GROOVY.parseClass("root.\"socket-binding-groups\".\"socket-binding-group\".find { it.@name == \"${defaultSocketBindingGroup}\" }");

@Override
public GPathResult locate(GPathResult root, OfflineOptions options) throws Exception {
boolean domain = Type.of(root) == Type.DOMAIN;

Script script = (Script) (
options.isDomain ? DOMAIN_SCRIPT_CLASS.newInstance() : STANDALONE_SCRIPT_CLASS.newInstance()
domain ? DOMAIN_SCRIPT_CLASS.newInstance() : STANDALONE_OR_HOST_SCRIPT_CLASS.newInstance()
);
script.setProperty("root", root);
if (options.isDomain) {
if (domain) {
String defaultSocketBindingGroup = options.defaultProfile + "-sockets";
if ("default".equals(options.defaultProfile)) {
defaultSocketBindingGroup = "standard-sockets";
Expand Down Expand Up @@ -285,7 +323,9 @@ private SubtreeCreator(String tagName, boolean skipInDomain) {
}

void addIfMissing(GPathResult root, OfflineOptions options) {
if (skipInDomain && options.isDomain) {
boolean domain = Type.of(root) == Type.DOMAIN;

if (skipInDomain && domain) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package org.wildfly.extras.creaper.commands.foundation.offline.xml;

import org.custommonkey.xmlunit.XMLUnit;
import org.junit.BeforeClass;
import org.junit.Test;
import org.xml.sax.SAXException;

import java.io.IOException;

import static org.wildfly.extras.creaper.XmlAssert.assertXmlIdentical;

public class FirstLevelXmlElementOrderTest {
private static final String DOMAIN_XML = ""
+ "<?xml version=\"1.0\" ?>\n"
+ "<domain xmlns=\"urn:jboss:domain:4.1\">\n"
+ " <extensions/>\n"
+ " <system-properties/>\n"
+ " <management/>\n"
+ " <profiles/>\n"
+ " <interfaces/>\n"
+ " <socket-binding-groups/>\n"
+ " <server-groups/>\n"
+ "</domain>";

private static final String HOST_XML = ""
+ "<?xml version=\"1.0\" ?>\n"
+ "<host xmlns=\"urn:jboss:domain:4.1\" name=\"master\">\n"
+ " <extensions/>\n"
+ " <management/>\n"
+ " <domain-controller/>\n"
+ " <interfaces/>\n"
+ " <jvms/>\n"
+ " <servers/>\n"
+ " <profile/>\n"
+ "</host>";

private static final String SERVER_XML = ""
+ "<?xml version=\"1.0\"?>\n"
+ "<server xmlns=\"urn:jboss:domain:4.1\">\n"
+ " <extensions/>\n"
+ " <management/>\n"
+ " <profile/>\n"
+ " <interfaces/>\n"
+ " <socket-binding-group/>\n"
+ "</server>";

private static final String UNKNOWN_XML = "<foobar/>";

@BeforeClass
public static void setUpXmlUnit() {
XMLUnit.setIgnoreWhitespace(true);
}

@Test
public void domain() throws IOException, SAXException {
assertXmlIdentical(DOMAIN_XML, FirstLevelXmlElementOrder.fix(DOMAIN_XML));
}

@Test
public void host() throws IOException, SAXException {
assertXmlIdentical(HOST_XML, FirstLevelXmlElementOrder.fix(HOST_XML));
}

@Test
public void server() throws IOException, SAXException {
assertXmlIdentical(SERVER_XML, FirstLevelXmlElementOrder.fix(SERVER_XML));
}

@Test(expected = IllegalArgumentException.class)
public void unknown() {
FirstLevelXmlElementOrder.fix(UNKNOWN_XML);
}
}

0 comments on commit fd9d09b

Please sign in to comment.