/*
* JBoss, Home of Professional Open Source.
* Copyright 2011, 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.
*/
package org.jboss.as.messaging;
import static java.util.Arrays.asList;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
import static org.jboss.as.controller.parsing.ParseUtils.missingRequired;
import static org.jboss.as.controller.parsing.ParseUtils.readStringAttributeElement;
import static org.jboss.as.controller.parsing.ParseUtils.requireNoAttributes;
import static org.jboss.as.controller.parsing.ParseUtils.requireSingleAttribute;
import static org.jboss.as.controller.parsing.ParseUtils.unexpectedAttribute;
import static org.jboss.as.controller.parsing.ParseUtils.unexpectedElement;
import static org.jboss.as.messaging.CommonAttributes.DEFAULT;
import static org.jboss.as.messaging.CommonAttributes.JMS_BRIDGE;
import static org.jboss.as.messaging.CommonAttributes.REMOTING_INCOMING_INTERCEPTORS;
import static org.jboss.as.messaging.CommonAttributes.REMOTING_OUTGOING_INTERCEPTORS;
import static org.jboss.as.messaging.CommonAttributes.SELECTOR;
import static org.jboss.as.messaging.Element.DISCOVERY_GROUP_REF;
import static org.jboss.as.messaging.Element.STATIC_CONNECTORS;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import javax.xml.stream.XMLStreamException;
import org.jboss.as.controller.AttributeDefinition;
import org.jboss.as.controller.ListAttributeDefinition;
import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.parsing.ParseUtils;
import org.jboss.as.messaging.jms.bridge.JMSBridgeDefinition;
import org.jboss.as.messaging.logging.MessagingLogger;
import org.jboss.dmr.ModelNode;
import org.jboss.staxmapper.XMLExtendedStreamReader;
/**
* Messaging subsystem 1.3 XML parser.
*
* @author <a href="http://jmesnil.net/">Jeff Mesnil</a>
*
*/
public class Messaging13SubsystemParser extends Messaging12SubsystemParser {
protected Messaging13SubsystemParser() {
}
@Override
protected void checkClusterConnectionConstraints(XMLExtendedStreamReader reader, Set<Element> seen) throws XMLStreamException {
// AS7-5598 relax constraints on the cluster-connection to accept one without static-connectors or discovery-group-ref
// however it is still not valid to have both
checkNotBothElements(reader, seen, STATIC_CONNECTORS, DISCOVERY_GROUP_REF);
}
@Override
protected void checkBroadcastGroupConstraints(XMLExtendedStreamReader reader, Set<Element> seen) throws XMLStreamException {
checkNotBothElements(reader, seen, Element.SOCKET_BINDING, Element.JGROUPS_STACK);
checkNotBothElements(reader, seen, Element.JGROUPS_STACK, Element.GROUP_ADDRESS);
checkNotBothElements(reader, seen, Element.GROUP_ADDRESS, Element.SOCKET_BINDING);
if (seen.contains(Element.GROUP_ADDRESS) && !seen.contains(Element.GROUP_PORT)) {
throw missingRequired(reader, EnumSet.of(Element.GROUP_PORT));
}
}
@Override
protected void checkDiscoveryGroupConstraints(XMLExtendedStreamReader reader, Set<Element> seen) throws XMLStreamException {
checkNotBothElements(reader, seen, Element.SOCKET_BINDING, Element.JGROUPS_STACK);
checkNotBothElements(reader, seen, Element.JGROUPS_STACK, Element.GROUP_ADDRESS);
checkNotBothElements(reader, seen, Element.GROUP_ADDRESS, Element.SOCKET_BINDING);
if (seen.contains(Element.GROUP_ADDRESS) && !seen.contains(Element.GROUP_PORT)) {
throw missingRequired(reader, EnumSet.of(Element.GROUP_PORT));
}
}
protected void handleUnknownConnectionFactoryAttribute(XMLExtendedStreamReader reader, Element element, ModelNode connectionFactory, boolean pooled)
throws XMLStreamException {
switch (element) {
case CALL_FAILOVER_TIMEOUT:
case COMPRESS_LARGE_MESSAGES:
handleElementText(reader, element, connectionFactory);
break;
case USE_AUTO_RECOVERY:
case INITIAL_MESSAGE_PACKET_SIZE:
if (!pooled) {
throw unexpectedElement(reader);
}
handleElementText(reader, element, connectionFactory);
break;
case INITIAL_CONNECT_ATTEMPTS:
if (!pooled) {
throw unexpectedElement(reader);
}
handleElementText(reader, element, "pooled", connectionFactory);
break;
default: {
super.handleUnknownConnectionFactoryAttribute(reader, element, connectionFactory, pooled);
}
}
}
@Override
protected void handleUnknownClusterConnectionAttribute(XMLExtendedStreamReader reader, Element element, ModelNode clusterConnectionAdd)
throws XMLStreamException {
switch (element) {
case CALL_FAILOVER_TIMEOUT:
case NOTIFICATION_ATTEMPTS:
case NOTIFICATION_INTERVAL:
handleElementText(reader, element, clusterConnectionAdd);
break;
default: {
super.handleUnknownClusterConnectionAttribute(reader, element, clusterConnectionAdd);
}
}
}
@Override
protected void handleUnknownConfigurationAttribute(XMLExtendedStreamReader reader, Element element, ModelNode operation) throws XMLStreamException {
switch (element) {
case CHECK_FOR_LIVE_SERVER:
case BACKUP_GROUP_NAME:
case REPLICATION_CLUSTERNAME:
handleElementText(reader, element, operation);
break;
default: {
super.handleUnknownConfigurationAttribute(reader, element, operation);
}
}
}
@Override
protected void handleComplexConfigurationAttribute(XMLExtendedStreamReader reader, Element element, ModelNode operation) throws XMLStreamException {
switch (element) {
case REMOTING_INCOMING_INTERCEPTORS:
processRemotingIncomingInterceptors(reader, operation);
break;
case REMOTING_OUTGOING_INTERCEPTORS:
processRemotingOutgoingInterceptors(reader, operation);
break;
default: {
super.handleComplexConfigurationAttribute(reader, element, operation);
}
}
}
@Override
protected void handleUnknownBroadcastGroupAttribute(XMLExtendedStreamReader reader, Element element, ModelNode operation)
throws XMLStreamException {
switch (element) {
case JGROUPS_STACK:
case JGROUPS_CHANNEL:
handleElementText(reader, element, operation);
break;
default: {
super.handleUnknownBroadcastGroupAttribute(reader, element, operation);
}
}
}
@Override
protected void handleUnknownDiscoveryGroupAttribute(XMLExtendedStreamReader reader, Element element, ModelNode operation)
throws XMLStreamException {
switch (element) {
case JGROUPS_STACK:
case JGROUPS_CHANNEL:
handleElementText(reader, element, operation);
break;
default: {
super.handleUnknownDiscoveryGroupAttribute(reader, element, operation);
}
}
}
protected void processHornetQServers(final XMLExtendedStreamReader reader, final ModelNode subsystemAddress, final List<ModelNode> list) throws XMLStreamException {
while (reader.hasNext() && reader.nextTag() != END_ELEMENT) {
final Namespace schemaVer = Namespace.forUri(reader.getNamespaceURI());
switch (schemaVer) {
case MESSAGING_1_0:
case UNKNOWN:
throw ParseUtils.unexpectedElement(reader);
default: {
final Element element = Element.forName(reader.getLocalName());
switch (element) {
case HORNETQ_SERVER:
processHornetQServer(reader, subsystemAddress, list, schemaVer);
break;
case JMS_BRIDGE:
processJmsBridge(reader, subsystemAddress, list);
break;
default:
throw ParseUtils.unexpectedElement(reader);
}
}
}
}
}
private void processJmsBridge(XMLExtendedStreamReader reader, ModelNode subsystemAddress, List<ModelNode> list) throws XMLStreamException {
String bridgeName = null;
String moduleName = null;
final int count = reader.getAttributeCount();
for (int n = 0; n < count; n++) {
String attrName = reader.getAttributeLocalName(n);
Attribute attribute = Attribute.forName(attrName);
switch (attribute) {
case NAME:
bridgeName = reader.getAttributeValue(n);
break;
case MODULE:
moduleName = reader.getAttributeValue(n);
break;
default:
throw unexpectedAttribute(reader, n);
}
}
if (bridgeName == null || bridgeName.length() == 0) {
bridgeName = DEFAULT;
}
final ModelNode address = subsystemAddress.clone();
address.add(JMS_BRIDGE, bridgeName);
address.protect();
final ModelNode operation = new ModelNode();
operation.get(OP).set(ADD);
operation.get(OP_ADDR).set(address);
list.add(operation);
if (moduleName != null && moduleName.length() > 0) {
JMSBridgeDefinition.MODULE.parseAndSetParameter(moduleName, operation, reader);
}
while(reader.hasNext() && reader.nextTag() != END_ELEMENT) {
final Element element = Element.forName(reader.getLocalName());
switch (element) {
case SOURCE:
case TARGET:
processJmsBridgeResource(reader, operation, element.getLocalName());
break;
case QUALITY_OF_SERVICE:
case FAILURE_RETRY_INTERVAL:
case MAX_RETRIES:
case MAX_BATCH_SIZE:
case MAX_BATCH_TIME:
case SUBSCRIPTION_NAME:
case CLIENT_ID:
case ADD_MESSAGE_ID_IN_HEADER:
handleElementText(reader, element, operation);
break;
case SELECTOR:
requireSingleAttribute(reader, CommonAttributes.STRING);
final String selector = readStringAttributeElement(reader, CommonAttributes.STRING);
SELECTOR.parseAndSetParameter(selector, operation, reader);
break;
default:
throw ParseUtils.unexpectedElement(reader);
}
}
}
private void processJmsBridgeResource(XMLExtendedStreamReader reader, ModelNode operation, String modelName) throws XMLStreamException {
while (reader.hasNext() && reader.nextTag() != END_ELEMENT) {
final Element element = Element.forName(reader.getLocalName());
switch (element) {
case USER:
case PASSWORD:
handleElementText(reader, element, modelName, operation);
break;
case CONNECTION_FACTORY:
case DESTINATION:
handleSingleAttribute(reader, element, modelName, CommonAttributes.NAME, operation);
break;
case CONTEXT:
ModelNode context = operation.get(element.getDefinition(modelName).getName());
processContext(reader, context);
break;
default:
throw ParseUtils.unexpectedElement(reader);
}
}
}
private void processRemotingIncomingInterceptors(XMLExtendedStreamReader reader, ModelNode operation) throws XMLStreamException {
requireNoAttributes(reader);
while(reader.hasNext() && reader.nextTag() != END_ELEMENT) {
final Element element = Element.forName(reader.getLocalName());
switch (element) {
case CLASS_NAME: {
final String value = reader.getElementText();
REMOTING_INCOMING_INTERCEPTORS.parseAndAddParameterElement(value, operation, reader);
break;
} default: {
throw ParseUtils.unexpectedElement(reader);
}
}
}
}
private void processRemotingOutgoingInterceptors(XMLExtendedStreamReader reader, ModelNode operation) throws XMLStreamException {
requireNoAttributes(reader);
while(reader.hasNext() && reader.nextTag() != END_ELEMENT) {
final Element element = Element.forName(reader.getLocalName());
switch (element) {
case CLASS_NAME: {
final String value = reader.getElementText();
REMOTING_OUTGOING_INTERCEPTORS.parseAndAddParameterElement(value, operation, reader);
break;
} default: {
throw ParseUtils.unexpectedElement(reader);
}
}
}
}
private void processContext(XMLExtendedStreamReader reader, ModelNode context) throws XMLStreamException {
while (reader.hasNext() && reader.nextTag() != END_ELEMENT) {
final Element element = Element.forName(reader.getLocalName());
switch (element) {
case PROPERTY:
int count = reader.getAttributeCount();
String key = null;
String value = null;
for (int n = 0; n < count; n++) {
String attrName = reader.getAttributeLocalName(n);
Attribute attribute = Attribute.forName(attrName);
switch (attribute) {
case KEY:
key = reader.getAttributeValue(n);
break;
case VALUE:
value = reader.getAttributeValue(n);
break;
default:
throw unexpectedAttribute(reader, n);
}
}
context.get(key).set(value);
ParseUtils.requireNoContent(reader);
break;
default:
throw ParseUtils.unexpectedElement(reader);
}
}
}
/**
* [AS7-5808] Support space-separated roles names for backwards compatibility and comma-separated ones for compatibility with
* HornetQ configuration.
*
* Roles are persisted using space character delimiter in {@link MessagingXMLWriter}.
*/
@Override
protected List<String> parseRolesAttribute(XMLExtendedStreamReader reader, int index) throws XMLStreamException {
String roles = reader.getAttributeValue(index);
return asList(roles.split("[,\\s]+"));
}
static void handleSingleAttribute(final XMLExtendedStreamReader reader, final Element element, final String modelName, String attributeName, final ModelNode node) throws XMLStreamException {
AttributeDefinition attributeDefinition = element.getDefinition(modelName);
final String value = readStringAttributeElement(reader, attributeName);
if (attributeDefinition instanceof SimpleAttributeDefinition) {
((SimpleAttributeDefinition) attributeDefinition).parseAndSetParameter(value, node, reader);
} else if (attributeDefinition instanceof ListAttributeDefinition) {
((ListAttributeDefinition) attributeDefinition).parseAndAddParameterElement(value, node, reader);
}
}
/**
* Check that not both elements have been defined
*/
protected static void checkNotBothElements(XMLExtendedStreamReader reader, Set<Element> seen, Element element1, Element element2) throws XMLStreamException {
if (seen.contains(element1) && seen.contains(element2)) {
throw new XMLStreamException(MessagingLogger.ROOT_LOGGER.onlyOneRequired(element1.getLocalName(), element2.getLocalName()), reader.getLocation());
}
}
}