/* * JBoss, Home of Professional Open Source. * Copyright 2015, 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.logging; 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.requireNoContent; import static org.jboss.as.controller.parsing.ParseUtils.requireNoNamespaceAttribute; import static org.jboss.as.controller.parsing.ParseUtils.unexpectedAttribute; import static org.jboss.as.controller.parsing.ParseUtils.unexpectedElement; import static org.jboss.as.logging.CommonAttributes.FILTER_PATTERN; import static org.jboss.as.logging.CommonAttributes.FILTER_SPEC; import static org.jboss.as.logging.CommonAttributes.MAX_INCLUSIVE; import static org.jboss.as.logging.CommonAttributes.MAX_LEVEL; import static org.jboss.as.logging.CommonAttributes.MIN_INCLUSIVE; import static org.jboss.as.logging.CommonAttributes.MIN_LEVEL; import static org.jboss.as.logging.CommonAttributes.NAME; import static org.jboss.as.logging.CommonAttributes.PROPERTIES; import static org.jboss.as.logging.CommonAttributes.REPLACEMENT; import static org.jboss.as.logging.CommonAttributes.REPLACE_ALL; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import java.util.Collections; import java.util.List; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.parsing.ParseUtils; import org.jboss.dmr.ModelNode; import org.jboss.staxmapper.XMLElementReader; import org.jboss.staxmapper.XMLExtendedStreamReader; /** * @author <a href="mailto:jperkins@redhat.com">James R. Perkins</a> */ abstract class LoggingSubsystemParser implements XMLStreamConstants, XMLElementReader<List<ModelNode>> { static final PathAddress SUBSYSTEM_ADDRESS = PathAddress.pathAddress(LoggingResourceDefinition.SUBSYSTEM_PATH); /** * Appends the key and value to the address and sets the address on the operation. * * @param operation the operation to set the address on * @param base the base address * @param key the key for the new address * @param value the value for the new address */ static void addOperationAddress(final ModelNode operation, final PathAddress base, final String key, final String value) { operation.get(OP_ADDR).set(base.append(key, value).toModelNode()); } /** * Reads the single {@code name} attribute from an element. * * @param reader the reader to use * * @return the value of the {@code name} attribute * * @throws XMLStreamException if the {@code name} attribute is not present, there is more than one attribute on the * element or there is content within the element. */ static String readNameAttribute(final XMLExtendedStreamReader reader) throws XMLStreamException { return readStringAttributeElement(reader, Attribute.NAME.getLocalName()); } /** * Reads the single {@code value} attribute from an element. * * @param reader the reader to use * * @return the value of the {@code value} attribute * * @throws XMLStreamException if the {@code value} attribute is not present, there is more than one attribute on the * element or there is content within the element. */ static String readValueAttribute(final XMLExtendedStreamReader reader) throws XMLStreamException { return readStringAttributeElement(reader, Attribute.VALUE.getLocalName()); } /** * Parses a property element. * <p> * Example of the expected XML: * <code> * <pre> * <properties> * <property name="propertyName" value="propertyValue"/> * </properties> * </pre> * </code> * * The {@code name} attribute is required. If the {@code value} attribute is not present an * {@linkplain org.jboss.dmr.ModelNode UNDEFINED ModelNode} will set on the operation. * </p> * * @param operation the operation to add the parsed properties to * @param reader the reader to use * * @throws XMLStreamException if a parsing error occurs */ static void parsePropertyElement(final ModelNode operation, final XMLExtendedStreamReader reader) throws XMLStreamException { while (reader.nextTag() != END_ELEMENT) { final int cnt = reader.getAttributeCount(); String name = null; String value = null; for (int i = 0; i < cnt; i++) { requireNoNamespaceAttribute(reader, i); final String attrValue = reader.getAttributeValue(i); final Attribute attribute = Attribute.forName(reader.getAttributeLocalName(i)); switch (attribute) { case NAME: { name = attrValue; break; } case VALUE: { value = attrValue; break; } default: throw unexpectedAttribute(reader, i); } } if (name == null) { throw missingRequired(reader, Collections.singleton(Attribute.NAME.getLocalName())); } operation.get(PROPERTIES.getName()).add(name, (value == null ? new ModelNode() : new ModelNode(value))); if (reader.nextTag() != END_ELEMENT) { throw unexpectedElement(reader); } } } /** * A helper to parse the deprecated {@code filter} for schema versions {@code 1.0} and {@code 1.1}. This parses the * XML and creates a {@code filter-spec} expression. The expression is set as the value for the {@code filter-spec} * attribute on the operation. * * @param operation the operation to add the parsed filter to * @param reader the reader used to read the filter * * @throws XMLStreamException if a parsing error occurs */ static void parseFilter(final ModelNode operation, final XMLExtendedStreamReader reader) throws XMLStreamException { final StringBuilder filter = new StringBuilder(); parseFilterChildren(filter, false, reader); operation.get(FILTER_SPEC.getName()).set(filter.toString()); } private static void parseFilterChildren(final StringBuilder filter, final boolean useDelimiter, final XMLExtendedStreamReader reader) throws XMLStreamException { // No attributes ParseUtils.requireNoAttributes(reader); final char delimiter = ','; // Elements while (reader.hasNext() && reader.nextTag() != END_ELEMENT) { final Element element = Element.forName(reader.getLocalName()); switch (element) { case ACCEPT: { filter.append(Filters.ACCEPT); requireNoContent(reader); break; } case ALL: { filter.append(Filters.ALL).append('('); parseFilterChildren(filter, true, reader); // If the last character is a delimiter remove it final int index = filter.length() - 1; if (filter.charAt(index) == delimiter) { filter.setCharAt(index, ')'); } else { filter.append(')'); } break; } case ANY: { filter.append(Filters.ANY).append('('); parseFilterChildren(filter, true, reader); // If the last character is a delimiter remove it final int index = filter.length() - 1; if (filter.charAt(index) == delimiter) { filter.setCharAt(index, ')'); } else { filter.append(')'); } break; } case CHANGE_LEVEL: { filter.append(Filters.LEVEL_CHANGE) .append('(') .append(readStringAttributeElement(reader, CommonAttributes.NEW_LEVEL.getName())) .append(')'); break; } case DENY: { filter.append(Filters.DENY); requireNoContent(reader); break; } case LEVEL: { filter.append(Filters.LEVELS) .append('(') .append(readStringAttributeElement(reader, NAME.getName())) .append(')'); break; } case LEVEL_RANGE: { filter.append(Filters.LEVEL_RANGE); final boolean minInclusive = Boolean.parseBoolean(reader.getAttributeValue(null, MIN_INCLUSIVE.getName())); final boolean maxInclusive = Boolean.parseBoolean(reader.getAttributeValue(null, MAX_INCLUSIVE.getName())); if (minInclusive) { filter.append('['); } else { filter.append('('); } filter.append(reader.getAttributeValue(null, MIN_LEVEL.getName())).append(delimiter); filter.append(reader.getAttributeValue(null, MAX_LEVEL.getName())); if (maxInclusive) { filter.append(']'); } else { filter.append(')'); } requireNoContent(reader); break; } case MATCH: { filter.append(Filters.MATCH).append("(\"").append(readStringAttributeElement(reader, FILTER_PATTERN.getName())).append("\")"); break; } case NOT: { filter.append(Filters.NOT).append('('); parseFilterChildren(filter, true, reader); // If the last character is a delimiter remove it final int index = filter.length() - 1; if (filter.charAt(index) == delimiter) { filter.setCharAt(index, ')'); } else { filter.append(')'); } break; } case REPLACE: { final boolean replaceAll = Boolean.valueOf(reader.getAttributeValue(null, REPLACE_ALL.getName())); if (replaceAll) { filter.append(Filters.SUBSTITUTE_ALL); } else { filter.append(Filters.SUBSTITUTE); } filter.append("(\"") .append(reader.getAttributeValue(null, FILTER_PATTERN.getName())) .append('"') .append(delimiter) .append('"') .append(reader.getAttributeValue(null, REPLACEMENT.getName())) .append("\")"); break; } default: throw unexpectedElement(reader); } if (useDelimiter) { filter.append(delimiter); } } } }