/* * JBoss, Home of Professional Open Source. * Copyright 2013, 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.controller.transform.description; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.jboss.as.controller.logging.ControllerLogger; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.transform.TransformationContext; import org.jboss.dmr.ModelNode; import org.jboss.dmr.ModelType; import org.jboss.dmr.Property; /** * Checks whether an attribute should be rejected or not * * @author <a href="kabir.khan@jboss.com">Kabir Khan</a> */ public interface RejectAttributeChecker { /** * Determines whether the given operation parameter value is not understandable by the target process and needs * to be rejected. * * @param address the address of the operation * @param attributeName the name of the attribute * @param attributeValue the value of the attribute * @param operation the operation executed. This is unmodifiable. * @param context the context of the transformation * @return {@code true} if the parameter value is not understandable by the target process and so needs to be rejected, {@code false} otherwise. */ boolean rejectOperationParameter(PathAddress address, String attributeName, ModelNode attributeValue, ModelNode operation, TransformationContext context); /** * Gets whether the given resource attribute value is not understandable by the target process and needs * to be rejected. * * @param address the address of the resource * @param attributeName the name of the attribute * @param attributeValue the value of the attribute * @param context the context of the transformation * @return {@code true} if the attribute value is not understandable by the target process and so needs to be rejected, {@code false} otherwise. */ boolean rejectResourceAttribute(PathAddress address, String attributeName, ModelNode attributeValue, TransformationContext context); /** * Returns the log message id used by this checker. This is used to group it so that all attributes failing a type of rejction * end up in the same error message * * @return the log message id */ String getRejectionLogMessageId(); /** * Gets the log message if the attribute failed rejection * * @param attributes a map of all attributes failed in this checker and their values * @return the formatted log message */ String getRejectionLogMessage(Map<String, ModelNode> attributes); /** * A standard implementation of RejectAttributeChecker. */ public abstract class DefaultRejectAttributeChecker implements RejectAttributeChecker { private volatile String logMessageId; /** * Constructor */ protected DefaultRejectAttributeChecker() { } /** * {@inheritDoc} */ public boolean rejectOperationParameter(PathAddress address, String attributeName, ModelNode attributeValue, ModelNode operation, TransformationContext context) { return rejectAttribute(address, attributeName, attributeValue, context); } /** * {@inheritDoc} */ public boolean rejectResourceAttribute(PathAddress address, String attributeName, ModelNode attributeValue, TransformationContext context) { return rejectAttribute(address, attributeName, attributeValue, context); } /** * Gets called by the default implementations of {@link #rejectOperationParameter(PathAddress, String, ModelNode, ModelNode, TransformationContext)} * and {@link #rejectResourceAttribute(PathAddress, String, ModelNode, TransformationContext)}. * * @param address the address of the operation * @param attributeName the name of the attribute * @param attributeValue the value of the attribute * @param context the context of the transformation * @return {@code true} if the attribute or parameter value is not understandable by the target process and so needs to be rejected, {@code false} otherwise. */ protected abstract boolean rejectAttribute(PathAddress address, String attributeName, ModelNode attributeValue, TransformationContext context); /** * Returns the log message id used by this checker. This is used to group it so that all attributes failing a type of rejction * end up in the same error message. This default implementation uses the formatted log message with an empty attribute map as the id. * * @return the log message id */ public String getRejectionLogMessageId() { String id = logMessageId; if (id == null) { id = getRejectionLogMessage(Collections.<String, ModelNode>emptyMap()); } logMessageId = id; return logMessageId; } } /** * Checks a simple attribute for expressions */ RejectAttributeChecker SIMPLE_EXPRESSIONS = new DefaultRejectAttributeChecker() { /** {@inheritDoc} */ @Override protected boolean rejectAttribute(PathAddress address, String attributeName, ModelNode attributeValue, TransformationContext context) { return checkForExpression(attributeValue); } /** * Check an attribute for expressions. * * @param node the attribute value * @return whether an expression was found or not */ private boolean checkForExpression(final ModelNode node) { if (!node.isDefined()) { return false; } final ModelNode resolved = node.clone(); if (node.getType() == ModelType.EXPRESSION || node.getType() == ModelType.STRING) { return checkForExpression(resolved.asString()); } else if (node.getType() == ModelType.OBJECT) { for (Property prop : resolved.asPropertyList()) { if (checkForExpression(prop.getValue())) { return true; } } } else if (node.getType() == ModelType.LIST) { for (ModelNode current : resolved.asList()) { if (checkForExpression(current)) { return true; } } } else if (node.getType() == ModelType.PROPERTY) { return checkForExpression(resolved.asProperty().getValue()); } return false; } private boolean checkForExpression(final String value) { return ExpressionPattern.EXPRESSION_PATTERN.matcher(value).matches(); } @Override public String getRejectionLogMessage(Map<String, ModelNode> attributes) { return ControllerLogger.ROOT_LOGGER.attributesDoNotSupportExpressions(attributes.keySet()); } }; /** * A RejectAttributeChecker for {@link ModelType#LIST} attribute values */ public class ListRejectAttributeChecker implements RejectAttributeChecker { private final RejectAttributeChecker elementChecker; /** * Constructor * * @param elementChecker the checker to check the list elements */ public ListRejectAttributeChecker(RejectAttributeChecker elementChecker) { this.elementChecker = elementChecker; } /** * {@inheritDoc} */ @Override public boolean rejectOperationParameter(PathAddress address, String attributeName, ModelNode attributeValue, ModelNode operation, TransformationContext context) { if (attributeValue.isDefined()) { for (ModelNode element : attributeValue.asList()) { if (elementChecker.rejectOperationParameter(address, attributeName, element, operation, context)) { return true; } } } return false; } /** * {@inheritDoc} */ @Override public boolean rejectResourceAttribute(PathAddress address, String attributeName, ModelNode attributeValue, TransformationContext context) { if (attributeValue.isDefined()) { for (ModelNode element : attributeValue.asList()) { if (elementChecker.rejectResourceAttribute(address, attributeName, element, context)) { return true; } } } return false; } public String getRejectionLogMessageId() { return elementChecker.getRejectionLogMessageId(); } @Override public String getRejectionLogMessage(Map<String, ModelNode> attributes) { return elementChecker.getRejectionLogMessage(attributes); } } /** * A RejectAttributeChecker for {@link ModelType#OBJECT} attribute values */ public class ObjectFieldsRejectAttributeChecker implements RejectAttributeChecker { private final Map<String, RejectAttributeChecker> fields = new HashMap<String, RejectAttributeChecker>(); /** * Constructor * * @param fields map of keys in the object type and the RejectAttributeChecker to use to check the entries */ public ObjectFieldsRejectAttributeChecker(Map<String, RejectAttributeChecker> fields) { assert fields != null : "Null fields"; assert fields.size() > 0 : "Empty fields"; this.fields.putAll(fields); } /** * {@inheritDoc} */ @Override public boolean rejectOperationParameter(PathAddress address, String attributeName, ModelNode attributeValue, ModelNode operation, TransformationContext context) { for (Map.Entry<String, RejectAttributeChecker> entry : fields.entrySet()) { ModelNode fieldValue = attributeValue.hasDefined(entry.getKey()) ? attributeValue.get(entry.getKey()) : new ModelNode(); if (entry.getValue().rejectOperationParameter(address, attributeName, fieldValue, operation, context)) { return true; } } return false; } /** * {@inheritDoc} */ @Override public boolean rejectResourceAttribute(PathAddress address, String attributeName, ModelNode attributeValue, TransformationContext context) { for (Map.Entry<String, RejectAttributeChecker> entry : fields.entrySet()) { ModelNode fieldValue = attributeValue.hasDefined(entry.getKey()) ? attributeValue.get(entry.getKey()) : new ModelNode(); if (entry.getValue().rejectResourceAttribute(address, attributeName, fieldValue, context)) { return true; } } return false; } @Override public String getRejectionLogMessageId() { return fields.entrySet().iterator().next().getValue().getRejectionLogMessageId(); } @Override public String getRejectionLogMessage(Map<String, ModelNode> attributes) { return fields.entrySet().iterator().next().getValue().getRejectionLogMessage(attributes); } } RejectAttributeChecker DEFINED = new DefaultRejectAttributeChecker() { @Override public String getRejectionLogMessage(Map<String, ModelNode> attributes) { return ControllerLogger.ROOT_LOGGER.attributesAreNotUnderstoodAndMustBeIgnored(attributes.keySet()); } @Override protected boolean rejectAttribute(PathAddress address, String attributeName, ModelNode attributeValue, TransformationContext context) { return attributeValue.isDefined(); } }; RejectAttributeChecker UNDEFINED = new DefaultRejectAttributeChecker() { @Override public String getRejectionLogMessage(Map<String, ModelNode> attributes) { return ControllerLogger.ROOT_LOGGER.attributesMustBeDefined(attributes.keySet()); } @Override protected boolean rejectAttribute(PathAddress address, String attributeName, ModelNode attributeValue, TransformationContext context) { return !attributeValue.isDefined(); } }; RejectAttributeChecker ALL = new DefaultRejectAttributeChecker() { @Override public String getRejectionLogMessage(Map<String, ModelNode> attributes) { return ControllerLogger.ROOT_LOGGER.attributesMustBeDefined(attributes.keySet()); } @Override protected boolean rejectAttribute(PathAddress address, String attributeName, ModelNode attributeValue, TransformationContext context) { return true; } }; /** * Rejects the attribute if the value is equal to the specified value. */ public static class SimpleRejectAttributeChecker extends DefaultRejectAttributeChecker { private final ModelNode value; public SimpleRejectAttributeChecker(ModelNode value) { this.value = value; } @Override public String getRejectionLogMessage(Map<String, ModelNode> attributes) { return ControllerLogger.ROOT_LOGGER.attributesMustNotBeDefinedAs(this.value, attributes.keySet()); } @Override protected boolean rejectAttribute(PathAddress address, String attributeName, ModelNode attributeValue, TransformationContext context) { return this.value.equals(attributeValue); } } /** * Rejects an attribute if the value is anything other than the specified value.. */ public static class SimpleAcceptAttributeChecker extends DefaultRejectAttributeChecker { private final ModelNode value; public SimpleAcceptAttributeChecker(ModelNode value) { this.value = value; } @Override public String getRejectionLogMessage(Map<String, ModelNode> attributes) { return ControllerLogger.ROOT_LOGGER.attributesMustBeDefinedAs(this.value, attributes.keySet()); } @Override protected boolean rejectAttribute(PathAddress address, String attributeName, ModelNode attributeValue, TransformationContext context) { return !this.value.equals(attributeValue); } } }