getAccessConstraints() {
- return accessConstraints;
- }
-
-
}
diff --git a/src/main/java/org/wildfly/extension/cassandra/RootDefinition.java b/src/main/java/org/wildfly/extension/cassandra/RootDefinition.java
index b044339..6e3e294 100644
--- a/src/main/java/org/wildfly/extension/cassandra/RootDefinition.java
+++ b/src/main/java/org/wildfly/extension/cassandra/RootDefinition.java
@@ -18,8 +18,8 @@
package org.wildfly.extension.cassandra;
import org.jboss.as.controller.AttributeDefinition;
-import org.jboss.as.controller.PersistentResourceDefinition;
import org.jboss.as.controller.ReloadRequiredRemoveStepHandler;
+import org.wildfly.extension.cassandra.future.PersistentResourceDefinition;
import java.util.Arrays;
import java.util.Collection;
diff --git a/src/main/java/org/wildfly/extension/cassandra/SubsystemParser.java b/src/main/java/org/wildfly/extension/cassandra/SubsystemParser.java
index 9e6bf90..af31faf 100644
--- a/src/main/java/org/wildfly/extension/cassandra/SubsystemParser.java
+++ b/src/main/java/org/wildfly/extension/cassandra/SubsystemParser.java
@@ -18,19 +18,20 @@
package org.wildfly.extension.cassandra;
import org.jboss.as.controller.PathAddress;
-import org.jboss.as.controller.PersistentResourceXMLDescription;
import org.jboss.as.controller.persistence.SubsystemMarshallingContext;
import org.jboss.dmr.ModelNode;
import org.jboss.staxmapper.XMLElementReader;
import org.jboss.staxmapper.XMLElementWriter;
import org.jboss.staxmapper.XMLExtendedStreamReader;
import org.jboss.staxmapper.XMLExtendedStreamWriter;
+import org.wildfly.extension.cassandra.future.PersistentResourceXMLDescription;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
-import java.util.List;
-import static org.jboss.as.controller.PersistentResourceXMLDescription.builder;
+import static org.wildfly.extension.cassandra.future.PersistentResourceXMLDescription.builder;
+
+import java.util.List;
/**
diff --git a/src/main/java/org/wildfly/extension/cassandra/future/AttributeMarshaller.java b/src/main/java/org/wildfly/extension/cassandra/future/AttributeMarshaller.java
new file mode 100644
index 0000000..142fcc1
--- /dev/null
+++ b/src/main/java/org/wildfly/extension/cassandra/future/AttributeMarshaller.java
@@ -0,0 +1,85 @@
+package org.wildfly.extension.cassandra.future;
+
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.ControllerMessages;
+import org.jboss.dmr.ModelNode;
+
+/**
+ * @author Tomaz Cerar
+ */
+public abstract class AttributeMarshaller {
+
+ /**
+ * Gets whether the given {@code resourceModel} has a value for this attribute that should be marshalled to XML.
+ *
+ * This is the same as {@code isMarshallable(resourceModel, true)}.
+ *
+ * @param attribute - attribute for which marshaling is being done
+ * @param resourceModel the model, a non-null node of {@link org.jboss.dmr.ModelType#OBJECT}.
+ * @return {@code true} if the given {@code resourceModel} has a defined value under this attribute's {@link AttributeDefinition#getName()} () name}.
+ */
+ public boolean isMarshallable(final AttributeDefinition attribute,final ModelNode resourceModel) {
+ return isMarshallable(attribute,resourceModel, true);
+ }
+
+ /**
+ * Gets whether the given {@code resourceModel} has a value for this attribute that should be marshalled to XML.
+ *
+ * @param attribute - attribute for which marshaling is being done
+ * @param resourceModel the model, a non-null node of {@link org.jboss.dmr.ModelType#OBJECT}.
+ * @param marshallDefault {@code true} if the value should be marshalled even if it matches the default value
+ * @return {@code true} if the given {@code resourceModel} has a defined value under this attribute's {@link AttributeDefinition#getName()} () name}
+ * and {@code marshallDefault} is {@code true} or that value differs from this attribute's {@link AttributeDefinition#getDefaultValue() default value}.
+ */
+ public boolean isMarshallable(final AttributeDefinition attribute, final ModelNode resourceModel, final boolean marshallDefault) {
+ return resourceModel.hasDefined(attribute.getName()) && (marshallDefault || !resourceModel.get(attribute.getName()).equals(attribute.getDefaultValue()));
+ }
+
+ /**
+ * Marshalls the value from the given {@code resourceModel} as an xml element, if it
+ * {@link #isMarshallable(AttributeDefinition, org.jboss.dmr.ModelNode, boolean) is marshallable}.
+ *
+ * @param attribute - attribute for which marshaling is being done
+ * @param resourceModel the model, a non-null node of {@link org.jboss.dmr.ModelType#OBJECT}.
+ * @param writer stream writer to use for writing the attribute
+ * @throws javax.xml.stream.XMLStreamException
+ * if thrown by {@code writer}
+ */
+
+ public void marshallAsAttribute(final AttributeDefinition attribute,final ModelNode resourceModel, final boolean marshallDefault, final XMLStreamWriter writer) throws XMLStreamException{
+ throw ControllerMessages.MESSAGES.cannotWriteTo(attribute.getName());
+ }
+
+ public void marshallAsElement(final AttributeDefinition attribute, final ModelNode resourceModel, final boolean marshallDefault, final XMLStreamWriter writer) throws XMLStreamException{
+ throw ControllerMessages.MESSAGES.cannotWriteTo(attribute.getName());
+ }
+
+ public boolean isMarshallableAsElement(){
+ return false;
+ }
+}
diff --git a/src/main/java/org/wildfly/extension/cassandra/future/AttributeParser.java b/src/main/java/org/wildfly/extension/cassandra/future/AttributeParser.java
new file mode 100644
index 0000000..56cb3ca
--- /dev/null
+++ b/src/main/java/org/wildfly/extension/cassandra/future/AttributeParser.java
@@ -0,0 +1,186 @@
+package org.wildfly.extension.cassandra.future;
+
+/*
+*
+* JBoss, Home of Professional Open Source.
+* Copyright 2014, Red Hat, Inc., and individual contributors
+* as indicated by the @author tags. See the copyright.txt file in the
+* distribution for a full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+* /
+*/
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.ListAttributeDefinition;
+import org.jboss.as.controller.MapAttributeDefinition;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.as.controller.operations.validation.ParameterValidator;
+import org.jboss.as.controller.parsing.ParseUtils;
+import org.jboss.dmr.ModelNode;
+import org.jboss.dmr.ModelType;
+
+/**
+* @author Tomaz Cerar (c) 2014 Red Hat Inc.
+*/
+public abstract class AttributeParser {
+
+ /**
+ * Creates a {@link org.jboss.dmr.ModelNode} using the given {@code value} after first validating the node
+ * against {@link org.jboss.as.controller.AttributeDefinition#getValidator() this object's validator}., and then stores it in the given {@code operation}
+ * model node as a key/value pair whose key is this attribute's getName() name}.
+ *
+ * If {@code value} is {@code null} an {@link org.jboss.dmr.ModelType#UNDEFINED undefined} node will be stored if such a value
+ * is acceptable to the validator.
+ *
+ *
+ * The expected usage of this method is in parsers seeking to build up an operation to store their parsed data
+ * into the configuration.
+ *
+ *
+ * @param value the value. Will be {@link String#trim() trimmed} before use if not {@code null}.
+ * @param operation model node of type {@link org.jboss.dmr.ModelType#OBJECT} into which the parsed value should be stored
+ * @param reader {@link javax.xml.stream.XMLStreamReader} from which the {@link javax.xml.stream.XMLStreamReader#getLocation() location} from which
+ * the attribute value was read can be obtained and used in any {@code XMLStreamException}, in case
+ * the given value is invalid.
+ * @throws javax.xml.stream.XMLStreamException if {@code value} is not valid
+ */
+ public void parseAndSetParameter(final AttributeDefinition attribute, final String value, final ModelNode operation, final XMLStreamReader reader) throws XMLStreamException {
+ ModelNode paramVal = parse(attribute, value, reader);
+ operation.get(attribute.getName()).set(paramVal);
+ }
+
+ /**
+ * Creates and returns a {@link org.jboss.dmr.ModelNode} using the given {@code value} after first validating the node
+ * against {@link org.jboss.as.controller.AttributeDefinition#getValidator() this object's validator}.
+ *
+ * If {@code value} is {@code null} an {@link org.jboss.dmr.ModelType#UNDEFINED undefined} node will be returned.
+ *
+ *
+ * @param value the value. Will be {@link String#trim() trimmed} before use if not {@code null}.
+ * @param reader {@link XMLStreamReader} from which the {@link XMLStreamReader#getLocation() location} from which
+ * the attribute value was read can be obtained and used in any {@code XMLStreamException}, in case
+ * the given value is invalid.
+ * @return {@code ModelNode} representing the parsed value
+ * @throws javax.xml.stream.XMLStreamException if {@code value} is not valid
+ * @see #parseAndSetParameter(org.jboss.as.controller.AttributeDefinition, String, ModelNode, XMLStreamReader)
+ */
+ public ModelNode parse(final AttributeDefinition attribute, final String value, final XMLStreamReader reader) throws XMLStreamException {
+ try {
+ return parse(attribute, value);
+ } catch (OperationFailedException e) {
+ throw new XMLStreamException(e.getFailureDescription().toString(), reader.getLocation());
+ }
+ }
+
+ private ModelNode parse(final AttributeDefinition attribute, final String value) throws OperationFailedException {
+ final String trimmed = value == null ? null : value.trim();
+ ModelNode node;
+ if (trimmed != null) {
+ if (attribute.isAllowExpression()) {
+ node = ParseUtils.parsePossibleExpression(trimmed);
+ } else {
+ node = new ModelNode().set(trimmed);
+ }
+ if (node.getType() != ModelType.EXPRESSION) {
+ // Convert the string to the expected type
+ switch (attribute.getType()) {
+ case BIG_DECIMAL:
+ node.set(node.asBigDecimal());
+ break;
+ case BIG_INTEGER:
+ node.set(node.asBigInteger());
+ break;
+ case BOOLEAN:
+ node.set(node.asBoolean());
+ break;
+ case BYTES:
+ node.set(node.asBytes());
+ break;
+ case DOUBLE:
+ node.set(node.asDouble());
+ break;
+ case INT:
+ node.set(node.asInt());
+ break;
+ case LONG:
+ node.set(node.asLong());
+ break;
+ }
+ }
+ } else {
+ node = new ModelNode();
+ }
+
+ final ParameterValidator validator;
+ // A bit yuck, but I didn't want to introduce a new type just for this
+ if (attribute instanceof ListAttributeDefinition) {
+ validator = ((ListAttributeDefinition) attribute).getElementValidator();
+ } else if (attribute instanceof MapAttributeDefinition) {
+ validator = ((MapAttributeDefinition) attribute).getValidator();
+ } else {
+ validator = attribute.getValidator();
+ }
+ validator.validateParameter(attribute.getXmlName(), node);
+
+ return node;
+ }
+
+ public static final AttributeParser SIMPLE = new AttributeParser() {
+ };
+
+ public static final AttributeParser LIST = new AttributeParser() {
+ @Override
+ public void parseAndSetParameter(AttributeDefinition attribute, String value, ModelNode operation, XMLStreamReader reader) throws XMLStreamException {
+ ModelNode paramVal = parse(attribute, value, reader);
+ operation.get(attribute.getName()).add(paramVal);
+ }
+ };
+
+ public static final AttributeParser STRING_LIST = new AttributeParser() {
+ @Override
+ public void parseAndSetParameter(AttributeDefinition attribute, String value, ModelNode operation, XMLStreamReader reader) throws XMLStreamException {
+ if (value == null) { return; }
+ for (String element : value.split(",")) {
+ parseAndAddParameterElement(attribute, element, operation, reader);
+ }
+ }
+
+ private void parseAndAddParameterElement(AttributeDefinition attribute, String value, ModelNode operation, XMLStreamReader reader) throws XMLStreamException {
+ ModelNode paramVal = parse(attribute, value, reader);
+ operation.get(attribute.getName()).add(paramVal);
+ }
+ };
+
+ public static final class DiscardOldDefaultValueParser extends AttributeParser{
+ private final String value;
+
+ public DiscardOldDefaultValueParser(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public ModelNode parse(AttributeDefinition attribute, String value, XMLStreamReader reader) throws XMLStreamException {
+ if (!this.value.equals(value)) { //if default value set, ignore it!
+ return super.parse(attribute, value, reader);
+ }
+ return new ModelNode();
+ }
+ }
+}
diff --git a/src/main/java/org/wildfly/extension/cassandra/future/DefaultAttributeMarshaller.java b/src/main/java/org/wildfly/extension/cassandra/future/DefaultAttributeMarshaller.java
new file mode 100644
index 0000000..34c2e1a
--- /dev/null
+++ b/src/main/java/org/wildfly/extension/cassandra/future/DefaultAttributeMarshaller.java
@@ -0,0 +1,63 @@
+package org.wildfly.extension.cassandra.future;
+
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.dmr.ModelNode;
+
+/**
+ * @author Tomaz Cerar (c) 2012 Red Hat Inc.
+ */
+public class DefaultAttributeMarshaller extends AttributeMarshaller {
+
+
+ public void marshallAsAttribute(final AttributeDefinition attribute, final ModelNode resourceModel, final boolean marshallDefault, final XMLStreamWriter writer) throws XMLStreamException {
+ if (isMarshallable(attribute, resourceModel, marshallDefault)) {
+ writer.writeAttribute(attribute.getXmlName(), resourceModel.get(attribute.getName()).asString());
+ }
+ }
+
+ public void marshallAsElement(AttributeDefinition attribute, ModelNode resourceModel, XMLStreamWriter writer) throws XMLStreamException {
+ marshallAsElement(attribute, resourceModel, true, writer);
+ }
+
+ @Override
+ public void marshallAsElement(final AttributeDefinition attribute, final ModelNode resourceModel, final boolean marshallDefault, final XMLStreamWriter writer) throws XMLStreamException {
+ if (isMarshallable(attribute, resourceModel, marshallDefault)) {
+ writer.writeStartElement(attribute.getXmlName());
+ String content = resourceModel.get(attribute.getName()).asString();
+ if (content.indexOf('\n') > -1) {
+ // Multiline content. Use the overloaded variant that staxmapper will format
+ writer.writeCharacters(content);
+ } else {
+ // Staxmapper will just output the chars without adding newlines if this is used
+ char[] chars = content.toCharArray();
+ writer.writeCharacters(chars, 0, chars.length);
+ }
+ writer.writeEndElement();
+ }
+ }
+}
diff --git a/src/main/java/org/wildfly/extension/cassandra/future/PersistentResourceDefinition.java b/src/main/java/org/wildfly/extension/cassandra/future/PersistentResourceDefinition.java
new file mode 100644
index 0000000..69d2831
--- /dev/null
+++ b/src/main/java/org/wildfly/extension/cassandra/future/PersistentResourceDefinition.java
@@ -0,0 +1,54 @@
+package org.wildfly.extension.cassandra.future;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.OperationStepHandler;
+import org.jboss.as.controller.PathElement;
+import org.jboss.as.controller.ReloadRequiredWriteAttributeHandler;
+import org.jboss.as.controller.SimpleResourceDefinition;
+import org.jboss.as.controller.descriptions.ResourceDescriptionResolver;
+import org.jboss.as.controller.registry.ManagementResourceRegistration;
+import org.jboss.as.controller.registry.OperationEntry;
+
+public abstract class PersistentResourceDefinition extends SimpleResourceDefinition {
+ protected PersistentResourceDefinition(PathElement pathElement, ResourceDescriptionResolver descriptionResolver) {
+ super(pathElement, descriptionResolver);
+ }
+
+ protected PersistentResourceDefinition(PathElement pathElement, ResourceDescriptionResolver descriptionResolver, OperationStepHandler addHandler, OperationStepHandler removeHandler) {
+ super(pathElement, descriptionResolver, addHandler, removeHandler);
+ }
+
+ protected PersistentResourceDefinition(PathElement pathElement, ResourceDescriptionResolver descriptionResolver, OperationStepHandler addHandler, OperationStepHandler removeHandler, OperationEntry.Flag addRestartLevel, OperationEntry.Flag removeRestartLevel) {
+ super(pathElement, descriptionResolver, addHandler, removeHandler, addRestartLevel, removeRestartLevel);
+ }
+
+
+ @Override
+ public void registerChildren(ManagementResourceRegistration resourceRegistration) {
+ super.registerChildren(resourceRegistration);
+ for (PersistentResourceDefinition child : getChildren()) {
+ resourceRegistration.registerSubModel(child);
+ }
+ }
+
+ @Override
+ public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
+ super.registerAttributes(resourceRegistration);
+ Collection attributeDefinitions = getAttributes();
+ AttributeDefinition[] attributes = attributeDefinitions.toArray(new AttributeDefinition[attributeDefinitions.size()]);
+ ReloadRequiredWriteAttributeHandler handler = new ReloadRequiredWriteAttributeHandler(attributes);
+ for (AttributeDefinition attr : getAttributes()) {
+ resourceRegistration.registerReadWriteAttribute(attr, null, handler);
+ }
+ }
+
+ protected List extends PersistentResourceDefinition> getChildren() {
+ return Collections.emptyList();
+ }
+
+ public abstract Collection getAttributes();
+}
diff --git a/src/main/java/org/wildfly/extension/cassandra/future/PersistentResourceXMLDescription.java b/src/main/java/org/wildfly/extension/cassandra/future/PersistentResourceXMLDescription.java
new file mode 100644
index 0000000..ae3d855
--- /dev/null
+++ b/src/main/java/org/wildfly/extension/cassandra/future/PersistentResourceXMLDescription.java
@@ -0,0 +1,334 @@
+package org.wildfly.extension.cassandra.future;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.XMLStreamException;
+
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.PathAddress;
+import org.jboss.as.controller.PathElement;
+import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
+import org.jboss.as.controller.operations.common.Util;
+import org.jboss.as.controller.parsing.Element;
+import org.jboss.as.controller.parsing.ParseUtils;
+import org.jboss.dmr.ModelNode;
+import org.jboss.dmr.Property;
+import org.jboss.staxmapper.XMLExtendedStreamReader;
+import org.jboss.staxmapper.XMLExtendedStreamWriter;
+
+import static javax.xml.stream.XMLStreamConstants.END_ELEMENT;
+import static org.jboss.as.controller.ControllerMessages.MESSAGES;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADDRESS;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.NAME;
+
+/**
+ * A representation of a resource as needed by the XML parser.
+ *
+ * @author Tomaz Cerar
+ * @author Stuart Douglas
+ */
+public class PersistentResourceXMLDescription {
+
+ protected static final AttributeMarshaller MARSHALLER = new DefaultAttributeMarshaller();
+
+ protected final PersistentResourceDefinition resourceDefinition;
+ protected final String xmlElementName;
+ protected final String xmlWrapperElement;
+ protected final LinkedHashMap attributes;
+ protected final List children;
+ protected final boolean useValueAsElementName;
+ protected final boolean noAddOperation;
+ protected final AdditionalOperationsGenerator additionalOperationsGenerator;
+ private boolean flushRequired = true;
+ private final Map attributeParsers;
+
+ protected PersistentResourceXMLDescription(final PersistentResourceDefinition resourceDefinition, final String xmlElementName, final String xmlWrapperElement, final LinkedHashMap attributes, final List children, final boolean useValueAsElementName, final boolean noAddOperation, final AdditionalOperationsGenerator additionalOperationsGenerator, Map attributeParsers) {
+ this.resourceDefinition = resourceDefinition;
+ this.xmlElementName = xmlElementName;
+ this.xmlWrapperElement = xmlWrapperElement;
+ this.attributes = attributes;
+ this.children = children;
+ this.useValueAsElementName = useValueAsElementName;
+ this.noAddOperation = noAddOperation;
+ this.additionalOperationsGenerator = additionalOperationsGenerator;
+ this.attributeParsers = attributeParsers;
+ }
+
+ public void parse(final XMLExtendedStreamReader reader, PathAddress parentAddress, List list) throws XMLStreamException {
+ if (xmlWrapperElement != null) {
+ if (reader.getLocalName().equals(xmlWrapperElement)) {
+ if (reader.hasNext()) {
+ if (reader.nextTag() == END_ELEMENT) {
+ return;
+ }
+ }
+ } else {
+ throw ParseUtils.unexpectedElement(reader);
+ }
+
+ }
+ boolean wildcard = resourceDefinition.getPathElement().isWildcard();
+ String name = null;
+ ModelNode op = Util.createAddOperation(parentAddress);
+ for (int i = 0; i < reader.getAttributeCount(); i++) {
+ String attributeName = reader.getAttributeLocalName(i);
+ String value = reader.getAttributeValue(i);
+ if (wildcard && NAME.equals(attributeName)) {
+ name = value;
+ } else if (attributes.containsKey(attributeName)) {
+ AttributeDefinition def = attributes.get(attributeName);
+ AttributeParser parser = attributeParsers.containsKey(attributeName)? attributeParsers.get(attributeName) : AttributeParser.SIMPLE;
+ assert parser != null;
+ parser.parseAndSetParameter(def,value,op,reader);
+ } else {
+ throw ParseUtils.unexpectedAttribute(reader, i);
+ }
+ }
+ if (wildcard && name == null) {
+ throw MESSAGES.missingRequiredAttributes(new StringBuilder(NAME), reader.getLocation());
+ }
+ PathElement path = wildcard ? PathElement.pathElement(resourceDefinition.getPathElement().getKey(), name) : resourceDefinition.getPathElement();
+ PathAddress address = parentAddress.append(path);
+ if(!noAddOperation) {
+ op.get(ADDRESS).set(address.toModelNode());
+ list.add(op);
+ }
+ if(additionalOperationsGenerator != null) {
+ additionalOperationsGenerator.additionalOperations(address, op, list);
+ }
+ parseChildren(reader, address, list);
+ if (xmlWrapperElement != null) {
+ ParseUtils.requireNoContent(reader);
+ }
+ }
+
+ private Map getChildrenMap() {
+ Map res = new HashMap<>();
+ for (PersistentResourceXMLDescription child : children) {
+ if (child.xmlWrapperElement != null) {
+ res.put(child.xmlWrapperElement, child);
+ } else {
+ res.put(child.xmlElementName, child);
+ }
+ }
+ return res;
+ }
+
+ public void parseChildren(final XMLExtendedStreamReader reader, PathAddress parentAddress, List list) throws XMLStreamException {
+ if (children.size() == 0) {
+ if (flushRequired){
+ ParseUtils.requireNoContent(reader);
+ }
+ } else {
+ Map children = getChildrenMap();
+ while (reader.hasNext() && reader.nextTag() != XMLStreamConstants.END_ELEMENT) {
+ PersistentResourceXMLDescription child = children.get(reader.getLocalName());
+ if (child != null) {
+ child.parse(reader, parentAddress, list);
+ } else {
+ throw ParseUtils.unexpectedElement(reader);
+ }
+ }
+ }
+ }
+
+
+ public void persist(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException {
+ persist(writer, model, null);
+ }
+
+ private void writeStartElement(XMLExtendedStreamWriter writer, String namespaceURI, String localName) throws XMLStreamException {
+ if (namespaceURI != null) {
+ writer.writeStartElement(namespaceURI, localName);
+ } else {
+ writer.writeStartElement(localName);
+ }
+ }
+
+ public void startSubsystemElement(XMLExtendedStreamWriter writer, String namespaceURI, boolean empty) throws XMLStreamException {
+
+ if (writer.getNamespaceContext().getPrefix(namespaceURI) == null) {
+ // Unknown namespace; it becomes default
+ writer.setDefaultNamespace(namespaceURI);
+ if (empty) {
+ writer.writeEmptyElement(Element.SUBSYSTEM.getLocalName());
+ } else {
+ writer.writeStartElement(Element.SUBSYSTEM.getLocalName());
+ }
+ writer.writeNamespace(null, namespaceURI);
+ } else {
+ if (empty) {
+ writer.writeEmptyElement(namespaceURI, Element.SUBSYSTEM.getLocalName());
+ } else {
+ writer.writeStartElement(namespaceURI, Element.SUBSYSTEM.getLocalName());
+ }
+ }
+
+ }
+
+ public void persist(XMLExtendedStreamWriter writer, ModelNode model, String namespaceURI) throws XMLStreamException {
+ boolean wildcard = resourceDefinition.getPathElement().isWildcard();
+ model = wildcard ? model.get(resourceDefinition.getPathElement().getKey()) : model.get(resourceDefinition.getPathElement().getKeyValuePair());
+ boolean isSubsystem = resourceDefinition.getPathElement().getKey().equals(ModelDescriptionConstants.SUBSYSTEM);
+ if (!isSubsystem && !model.isDefined() && !useValueAsElementName) {
+ return;
+ }
+
+ boolean writeWrapper = xmlWrapperElement != null;
+ if (writeWrapper) {
+ writeStartElement(writer, namespaceURI, xmlWrapperElement);
+ }
+
+ if (wildcard) {
+ for (Property p : model.asPropertyList()) {
+ if (useValueAsElementName) {
+ writeStartElement(writer, namespaceURI, p.getName());
+ } else {
+ writeStartElement(writer, namespaceURI, xmlElementName);
+ writer.writeAttribute(NAME, p.getName());
+ }
+ for (Map.Entry def : attributes.entrySet()) {
+ MARSHALLER.marshallAsAttribute(def.getValue(), p.getValue(), false, writer);
+ }
+ persistChildren(writer, p.getValue());
+ writer.writeEndElement();
+ }
+ } else {
+ if (useValueAsElementName) {
+ writeStartElement(writer, namespaceURI, resourceDefinition.getPathElement().getValue());
+ } else if (isSubsystem) {
+ startSubsystemElement(writer, namespaceURI, children.isEmpty());
+ } else {
+ writeStartElement(writer, namespaceURI, xmlElementName);
+
+ }
+ for (Map.Entry def : attributes.entrySet()) {
+ MARSHALLER.marshallAsAttribute(def.getValue(), model, true, writer);
+ }
+ persistChildren(writer, model);
+
+ // Do not attempt to write end element if the has no elements!
+ if (!isSubsystem || !children.isEmpty()) {
+ writer.writeEndElement();
+ }
+ }
+
+ if (writeWrapper) {
+ writer.writeEndElement();
+ }
+ }
+
+ public void persistChildren(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException {
+ for (PersistentResourceXMLDescription child : children) {
+ child.persist(writer, model);
+ }
+ }
+
+ public static PersistentResourceXMLBuilder builder(PersistentResourceDefinition resource) {
+ return new PersistentResourceXMLBuilder(resource);
+ }
+
+ public static class PersistentResourceXMLBuilder {
+
+
+ protected final PersistentResourceDefinition resourceDefinition;
+ protected String xmlElementName;
+ protected String xmlWrapperElement;
+ protected boolean useValueAsElementName;
+ protected boolean noAddOperation;
+ protected AdditionalOperationsGenerator additionalOperationsGenerator;
+ protected final LinkedHashMap attributes = new LinkedHashMap<>();
+ protected final List children = new ArrayList<>();
+ protected final LinkedHashMap attributeParsers = new LinkedHashMap<>();
+
+ protected PersistentResourceXMLBuilder(final PersistentResourceDefinition resourceDefinition) {
+ this.resourceDefinition = resourceDefinition;
+ this.xmlElementName = resourceDefinition.getPathElement().isWildcard() ? resourceDefinition.getPathElement().getKey() : resourceDefinition.getPathElement().getValue();
+ }
+
+ public PersistentResourceXMLBuilder addChild(PersistentResourceXMLBuilder builder) {
+ this.children.add(builder);
+ return this;
+ }
+
+ public PersistentResourceXMLBuilder addAttribute(AttributeDefinition attribute) {
+ this.attributes.put(attribute.getXmlName(), attribute);
+ return this;
+ }
+ public PersistentResourceXMLBuilder addAttribute(AttributeDefinition attribute, AttributeParser attributeParser) {
+ this.attributes.put(attribute.getXmlName(), attribute);
+ this.attributeParsers.put(attribute.getXmlName(),attributeParser);
+ return this;
+ }
+
+ public PersistentResourceXMLBuilder addAttributes(AttributeDefinition... attributes) {
+ for (final AttributeDefinition at : attributes) {
+ this.attributes.put(at.getXmlName(), at);
+ }
+ return this;
+ }
+
+ @Deprecated
+ public PersistentResourceXMLBuilder addAttributes(Collection extends AttributeDefinition> attributes) {
+ for (final AttributeDefinition at : attributes) {
+ this.attributes.put(at.getXmlName(), at);
+ }
+ return this;
+ }
+
+ public PersistentResourceXMLBuilder setXmlWrapperElement(final String xmlWrapperElement) {
+ this.xmlWrapperElement = xmlWrapperElement;
+ return this;
+ }
+
+ public PersistentResourceXMLBuilder setXmlElementName(final String xmlElementName) {
+ this.xmlElementName = xmlElementName;
+ return this;
+ }
+
+ public PersistentResourceXMLBuilder setUseValueAsElementName(final boolean useValueAsElementName) {
+ this.useValueAsElementName = useValueAsElementName;
+ return this;
+ }
+
+ public PersistentResourceXMLBuilder setNoAddOperation(final boolean noAddOperation) {
+ this.noAddOperation = noAddOperation;
+ return this;
+ }
+
+ public PersistentResourceXMLBuilder setAdditionalOperationsGenerator(final AdditionalOperationsGenerator additionalOperationsGenerator) {
+ this.additionalOperationsGenerator = additionalOperationsGenerator;
+ return this;
+ }
+
+ public PersistentResourceXMLDescription build() {
+
+ List builtChildren = new ArrayList<>();
+ for (PersistentResourceXMLBuilder b : children) {
+ builtChildren.add(b.build());
+ }
+ return new PersistentResourceXMLDescription(resourceDefinition, xmlElementName, xmlWrapperElement, attributes, builtChildren, useValueAsElementName, noAddOperation, additionalOperationsGenerator, attributeParsers);
+ }
+ }
+
+ /**
+ * Some resources require more operations that just a simple add. This interface provides a hook for these to be plugged in.
+ */
+ public interface AdditionalOperationsGenerator {
+
+ /**
+ * Generates any additional operations required by the resource
+ * @param address The address of the resource
+ * @param addOperation The add operation for the resource
+ * @param operations The operation list
+ */
+ void additionalOperations(final PathAddress address, final ModelNode addOperation, final List operations);
+
+ }
+}
diff --git a/src/main/java/org/wildfly/extension/cassandra/future/ServiceRemoveStepHandler.java b/src/main/java/org/wildfly/extension/cassandra/future/ServiceRemoveStepHandler.java
new file mode 100644
index 0000000..301f4d5
--- /dev/null
+++ b/src/main/java/org/wildfly/extension/cassandra/future/ServiceRemoveStepHandler.java
@@ -0,0 +1,71 @@
+package org.wildfly.extension.cassandra.future;
+
+import java.util.ArrayList;
+
+import org.jboss.as.controller.AbstractAddStepHandler;
+import org.jboss.as.controller.AbstractRemoveStepHandler;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.as.controller.PathAddress;
+import org.jboss.as.controller.ServiceVerificationHandler;
+import org.jboss.dmr.ModelNode;
+import org.jboss.msc.service.ServiceController;
+import org.jboss.msc.service.ServiceName;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
+
+/**
+ * Abstract remove step handler that simply removes a service. If the operation is rolled
+ * back it delegates the rollback to the corresponding add operations
+ * {@link AbstractAddStepHandler#performRuntime(OperationContext, org.jboss.dmr.ModelNode, org.jboss.dmr.ModelNode, ServiceVerificationHandler, java.util.List)}
+ * method
+ *
+ * @author Stuart Douglas
+ */
+public class ServiceRemoveStepHandler extends AbstractRemoveStepHandler {
+
+ private final ServiceName baseServiceName;
+ private final AbstractAddStepHandler addOperation;
+
+ public ServiceRemoveStepHandler(final ServiceName baseServiceName, final AbstractAddStepHandler addOperation) {
+ this.baseServiceName = baseServiceName;
+ this.addOperation = addOperation;
+ }
+
+ protected ServiceRemoveStepHandler(final AbstractAddStepHandler addOperation) {
+ this(null, addOperation);
+ }
+
+ protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) {
+ if (context.isResourceServiceRestartAllowed()) {
+ final PathAddress address = PathAddress.pathAddress(operation.require(OP_ADDR));
+ final String name = address.getLastElement().getValue();
+ context.removeService(serviceName(name, address));
+ } else {
+ context.reloadRequired();
+ }
+ }
+
+ /**
+ * The service name to be removed. Can be overridden for unusual service naming patterns
+ * @param name The name of the resource being removed
+ * @param address The address of the resource being removed
+ * @return The service name to remove
+ */
+ protected ServiceName serviceName(String name, PathAddress address) {
+ return serviceName(name);
+ }
+
+ /**
+ * The service name to be removed. Can be overridden for unusual service naming patterns
+ * @param name The name of the resource being removed
+ * @return The service name to remove
+ */
+ protected ServiceName serviceName(final String name) {
+ return baseServiceName.append(name);
+ }
+
+ protected void recoverServices(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
+ context.revertReloadRequired();
+ }
+}
diff --git a/src/main/resources/module/main/module.xml b/src/main/resources/module/main/module.xml
index b451251..5a10eeb 100644
--- a/src/main/resources/module/main/module.xml
+++ b/src/main/resources/module/main/module.xml
@@ -15,7 +15,7 @@
~ limitations under the License.
-->
-
+
diff --git a/src/test/java/org/wildfly/extension/cassandra/SubsystemParsingTestCase.java b/src/test/java/org/wildfly/extension/cassandra/SubsystemParsingTestCase.java
index bda4307..412189c 100644
--- a/src/test/java/org/wildfly/extension/cassandra/SubsystemParsingTestCase.java
+++ b/src/test/java/org/wildfly/extension/cassandra/SubsystemParsingTestCase.java
@@ -82,7 +82,7 @@ public void testParseSubsystem() throws Exception {
public void testInstallIntoController() throws Exception {
//Parse the subsystem xml and install into the controller
String subsystemXml = getSubsystemXml();
- KernelServices services = super.createKernelServicesBuilder(null).setSubsystemXml(subsystemXml).build();
+ KernelServices services = super.installInController(subsystemXml);
//Read the whole model and make sure it looks as expected
ModelNode model = services.readWholeModel();
@@ -97,13 +97,13 @@ public void testInstallIntoController() throws Exception {
public void testParseAndMarshalModel() throws Exception {
//Parse the subsystem xml and install into the first controller
String subsystemXml = getSubsystemXml();
- KernelServices servicesA = super.createKernelServicesBuilder(null).setSubsystemXml(subsystemXml).build();
+ KernelServices servicesA = super.installInController(subsystemXml);
//Get the model and the persisted xml from the first controller
ModelNode modelA = servicesA.readWholeModel();
String marshalled = servicesA.getPersistedSubsystemXml();
//Install the persisted xml from the first controller into a second controller
- KernelServices servicesB = super.createKernelServicesBuilder(null).setSubsystemXml(marshalled).build();
+ KernelServices servicesB = super.installInController(marshalled);
ModelNode modelB = servicesB.readWholeModel();
//Make sure the models from the two controllers are identical
@@ -117,7 +117,7 @@ public void testParseAndMarshalModel() throws Exception {
public void testSubsystemRemoval() throws Exception {
//Parse the subsystem xml and install into the first controller
String subsystemXml = getSubsystemXml();
- KernelServices services = super.createKernelServicesBuilder(null).setSubsystemXml(subsystemXml).build();
+ KernelServices services = super.installInController(subsystemXml);
//Checks that the subsystem was removed from the model
super.assertRemoveSubsystemResources(services);