private void parseProtocolElement(
     XMLExtendedStreamReader reader, PathAddress address, Map<PathAddress, ModelNode> operations)
     throws XMLStreamException {
   XMLElement element = XMLElement.forName(reader.getLocalName());
   switch (element) {
     case PROPERTY:
       {
         this.parseProperty(reader, address, operations);
         break;
       }
     case DEFAULT_THREAD_POOL:
       parseThreadPool(ThreadPoolResourceDefinition.DEFAULT, reader, address, operations);
       break;
     case INTERNAL_THREAD_POOL:
       parseThreadPool(ThreadPoolResourceDefinition.INTERNAL, reader, address, operations);
       break;
     case OOB_THREAD_POOL:
       parseThreadPool(ThreadPoolResourceDefinition.OOB, reader, address, operations);
       break;
     case TIMER_THREAD_POOL:
       parseThreadPool(ThreadPoolResourceDefinition.TIMER, reader, address, operations);
       break;
     default:
       {
         throw ParseUtils.unexpectedElement(reader);
       }
   }
 }
  private void parseChannel(
      XMLExtendedStreamReader reader,
      PathAddress subsystemAddress,
      Map<PathAddress, ModelNode> operations)
      throws XMLStreamException {
    String name = require(reader, XMLAttribute.NAME);
    PathAddress address = subsystemAddress.append(ChannelResourceDefinition.pathElement(name));
    ModelNode operation = Util.createAddOperation(address);
    operations.put(address, operation);

    for (int i = 0; i < reader.getAttributeCount(); i++) {
      ParseUtils.requireNoNamespaceAttribute(reader, i);
      XMLAttribute attribute = XMLAttribute.forName(reader.getAttributeLocalName(i));
      switch (attribute) {
        case NAME:
          {
            // Already parsed
            break;
          }
        case STACK:
          {
            readAttribute(reader, i, operation, ChannelResourceDefinition.Attribute.STACK);
            break;
          }
        case MODULE:
          {
            readAttribute(reader, i, operation, ChannelResourceDefinition.Attribute.MODULE);
            break;
          }
        default:
          {
            throw ParseUtils.unexpectedAttribute(reader, i);
          }
      }
    }

    while (reader.hasNext() && (reader.nextTag() != XMLStreamConstants.END_ELEMENT)) {
      XMLElement element = XMLElement.forName(reader.getLocalName());
      switch (element) {
        case FORK:
          {
            this.parseFork(reader, address, operations);
            break;
          }
        default:
          {
            throw ParseUtils.unexpectedElement(reader);
          }
      }
    }
  }
  private void parseRelay(
      XMLExtendedStreamReader reader,
      PathAddress stackAddress,
      Map<PathAddress, ModelNode> operations)
      throws XMLStreamException {
    PathAddress address = stackAddress.append(RelayResourceDefinition.PATH);
    ModelNode operation = Util.createAddOperation(address);
    operations.put(address, operation);

    for (int i = 0; i < reader.getAttributeCount(); i++) {
      XMLAttribute attribute = XMLAttribute.forName(reader.getAttributeLocalName(i));
      switch (attribute) {
        case SITE:
          {
            readAttribute(reader, i, operation, RelayResourceDefinition.Attribute.SITE);
            break;
          }
        default:
          {
            throw ParseUtils.unexpectedAttribute(reader, i);
          }
      }
    }

    if (!operation.hasDefined(RelayResourceDefinition.Attribute.SITE.getDefinition().getName())) {
      throw ParseUtils.missingRequired(reader, EnumSet.of(XMLAttribute.SITE));
    }

    while (reader.hasNext() && (reader.nextTag() != XMLStreamConstants.END_ELEMENT)) {
      XMLElement element = XMLElement.forName(reader.getLocalName());
      switch (element) {
        case REMOTE_SITE:
          {
            this.parseRemoteSite(reader, address, operations);
            break;
          }
        case PROPERTY:
          {
            this.parseProperty(reader, address, operations);
            break;
          }
        default:
          {
            throw ParseUtils.unexpectedElement(reader);
          }
      }
    }
  }
  @SuppressWarnings("deprecation")
  private void parseStacks(
      XMLExtendedStreamReader reader, PathAddress address, Map<PathAddress, ModelNode> operations)
      throws XMLStreamException {

    ModelNode operation = operations.get(address);

    for (int i = 0; i < reader.getAttributeCount(); i++) {
      ParseUtils.requireNoNamespaceAttribute(reader, i);
      XMLAttribute attribute = XMLAttribute.forName(reader.getAttributeLocalName(i));
      switch (attribute) {
        case DEFAULT:
          {
            readAttribute(
                reader, i, operation, JGroupsSubsystemResourceDefinition.Attribute.DEFAULT_STACK);
            break;
          }
        default:
          {
            throw ParseUtils.unexpectedAttribute(reader, i);
          }
      }
    }

    while (reader.hasNext() && (reader.nextTag() != XMLStreamConstants.END_ELEMENT)) {
      XMLElement element = XMLElement.forName(reader.getLocalName());
      switch (element) {
        case STACK:
          {
            this.parseStack(reader, address, operations);
            break;
          }
        default:
          {
            throw ParseUtils.unexpectedElement(reader);
          }
      }
    }
  }
  private void parseStack(
      XMLExtendedStreamReader reader,
      PathAddress subsystemAddress,
      Map<PathAddress, ModelNode> operations)
      throws XMLStreamException {
    String name = require(reader, XMLAttribute.NAME);
    PathAddress address = subsystemAddress.append(StackResourceDefinition.pathElement(name));
    ModelNode operation = Util.createAddOperation(address);
    operations.put(address, operation);

    while (reader.hasNext() && (reader.nextTag() != XMLStreamConstants.END_ELEMENT)) {
      XMLElement element = XMLElement.forName(reader.getLocalName());
      switch (element) {
        case TRANSPORT:
          {
            this.parseTransport(reader, address, operations);
            break;
          }
        case PROTOCOL:
          {
            this.parseProtocol(reader, address, operations);
            break;
          }
        case RELAY:
          {
            if (this.schema.since(JGroupsSchema.VERSION_2_0)) {
              this.parseRelay(reader, address, operations);
              break;
            }
          }
        default:
          {
            throw ParseUtils.unexpectedElement(reader);
          }
      }
    }
  }
  @SuppressWarnings("deprecation")
  @Override
  public void readElement(XMLExtendedStreamReader reader, List<ModelNode> result)
      throws XMLStreamException {

    Map<PathAddress, ModelNode> operations = new LinkedHashMap<>();

    PathAddress address = PathAddress.pathAddress(JGroupsSubsystemResourceDefinition.PATH);
    ModelNode operation = Util.createAddOperation(address);
    operations.put(address, operation);

    if (!this.schema.since(JGroupsSchema.VERSION_3_0)) {
      String defaultStack = require(reader, XMLAttribute.DEFAULT_STACK);
      setAttribute(
          reader,
          defaultStack,
          operation,
          JGroupsSubsystemResourceDefinition.Attribute.DEFAULT_STACK);
    }

    while (reader.hasNext() && (reader.nextTag() != XMLStreamConstants.END_ELEMENT)) {
      XMLElement element = XMLElement.forName(reader.getLocalName());
      switch (element) {
        case CHANNELS:
          {
            if (this.schema.since(JGroupsSchema.VERSION_3_0)) {
              this.parseChannels(reader, address, operations);
              break;
            }
          }
        case STACKS:
          {
            if (this.schema.since(JGroupsSchema.VERSION_3_0)) {
              this.parseStacks(reader, address, operations);
              break;
            }
          }
        case STACK:
          {
            if (!this.schema.since(JGroupsSchema.VERSION_3_0)) {
              this.parseStack(reader, address, operations);
              break;
            }
          }
        default:
          {
            throw ParseUtils.unexpectedElement(reader);
          }
      }
    }

    // Version prior to 4_0 schema did not require stack being defined,
    // thus iterate over channel add operations and set the stack explicitly.
    if (!this.schema.since(JGroupsSchema.VERSION_4_0)) {
      ModelNode defaultStack =
          operation.get(
              JGroupsSubsystemResourceDefinition.Attribute.DEFAULT_STACK.getDefinition().getName());

      for (Map.Entry<PathAddress, ModelNode> entry : operations.entrySet()) {
        PathAddress opAddr = entry.getKey();
        if (opAddr
            .getLastElement()
            .getKey()
            .equals(ChannelResourceDefinition.WILDCARD_PATH.getKey())) {
          ModelNode op = entry.getValue();
          if (!op.hasDefined(ChannelResourceDefinition.Attribute.STACK.getDefinition().getName())) {
            op.get(ChannelResourceDefinition.Attribute.STACK.getDefinition().getName())
                .set(defaultStack);
          }
        }
      }
    }

    result.addAll(operations.values());
  }