/* * * 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; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import org.jboss.as.controller.logging.ControllerLogger; import org.jboss.as.controller.ModelVersion; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.PathElement; import org.jboss.dmr.ModelNode; import org.jboss.logging.Logger; /** * Logger utility class that provides a unified mechanism to log warnings that occur as part of the transformation process. * <p/> * All log messages are queued for time of transformation and then written in the log as a single entry for all problems that occurred. * This way it is simple to see what potential problems could happen for each host that is of different version than the domain controller * <p/> * Sample output would look like this: * There were some problems during transformation process for target host: 'host-name' * Problems found: * Transforming operation %s at resource %s to subsystem '%s' model version '%s' -- attributes %s attributes were rejected * Transforming operation %s at resource %s to core model '%s' model version '%s' -- attributes %s attributes were rejected * * @author <a href="mailto:tomaz.cerar@redhat.com">Tomaz Cerar</a> (c) 2013 Red Hat Inc. */ public class TransformersLogger { private TransformationTarget target; private ControllerLogger logger; private List<LogEntry> messageQueue = Collections.synchronizedList(new LinkedList<LogEntry>()); private static final ConcurrentHashMap<String, TransformersLogger> loggers = new ConcurrentHashMap<String, TransformersLogger>(); private TransformersLogger(TransformationTarget target) { this.target = target; logger = Logger.getMessageLogger(ControllerLogger.class, "org.jboss.as.controller.transformer." + target.getHostName()); } public static TransformersLogger getLogger(TransformationTarget target){ String hostName = target.getHostName()==null?"<unknown>":target.getHostName(); TransformersLogger result = loggers.get(hostName); if (result == null) { result = new TransformersLogger(target); TransformersLogger existing = loggers.putIfAbsent(hostName, result); result = existing == null ? result : existing; } return result; } private static String findSubsystemName(PathAddress pathAddress) { for (PathElement element : pathAddress) { if (element.getKey().equals(SUBSYSTEM)) { return element.getValue(); } } return null; } /** * Log a warning for the resource at the provided address and a single attribute. The detail message is a default * 'Attributes are not understood in the target model version and this resource will need to be ignored on the target host.' * * @param address where warning occurred * @param attribute attribute we are warning about */ public void logAttributeWarning(PathAddress address, String attribute) { logAttributeWarning(address, null, null, attribute); } /** * Log a warning for the resource at the provided address and the given attributes. The detail message is a default * 'Attributes are not understood in the target model version and this resource will need to be ignored on the target host.' * * @param address where warning occurred * @param attributes attributes we are warning about */ public void logAttributeWarning(PathAddress address, Set<String> attributes) { logAttributeWarning(address, null, null, attributes); } /** * Log warning for the resource at the provided address and single attribute, using the provided detail * message. * * @param address where warning occurred * @param message custom error message to append * @param attribute attribute we are warning about */ public void logAttributeWarning(PathAddress address, String message, String attribute) { logAttributeWarning(address, null, message, attribute); } /** * Log a warning for the resource at the provided address and the given attributes, using the provided detail * message. * * @param address where warning occurred * @param message custom error message to append * @param attributes attributes we that have problems about */ public void logAttributeWarning(PathAddress address, String message, Set<String> attributes) { messageQueue.add(new AttributeLogEntry(address, null, message, attributes)); } /** * Log a warning for the given operation at the provided address for the given attribute, using the provided detail * message. * * @param address where warning occurred * @param operation where which problem occurred * @param message custom error message to append * @param attribute attribute we that has problem */ public void logAttributeWarning(PathAddress address, ModelNode operation, String message, String attribute) { messageQueue.add(new AttributeLogEntry(address, operation, message, attribute)); } /** * Log a warning for the given operation at the provided address for the given attributes, using the provided detail * message. * * @param address where warning occurred * @param operation where which problem occurred * @param message custom error message to append * @param attributes attributes we that have problems about */ public void logAttributeWarning(PathAddress address, ModelNode operation, String message, Set<String> attributes) { messageQueue.add(new AttributeLogEntry(address, operation, message, attributes)); } /** * Get a warning message for the given operation at the provided address for the passed attributes with the given * custom message appended. Intended for use in providing a failure description for an operation * or an exception message for an {@link org.jboss.as.controller.OperationFailedException}. * * @param address where warning occurred * @param operation where which problem occurred * @param message custom error message to append * @param attributes attributes we that have problems about */ public String getAttributeWarning(PathAddress address, ModelNode operation, String message, Set<String> attributes) { return new AttributeLogEntry(address, operation, message, attributes).getMessage(); } /** * Get a warning message for the given operation at the provided address for the passed attributes with the given * custom message appended. Intended for use in providing a failure description for an operation * or an exception message for an {@link org.jboss.as.controller.OperationFailedException}. * * @param address where warning occurred * @param operation where which problem occurred * @param message custom error message to append * @param attributes attributes we that have problems about */ private String getAttributeWarning(PathAddress address, ModelNode operation, String message, String... attributes) { return new AttributeLogEntry(address, operation, message, attributes).getMessage(); } /** * Get a warning message for the given operation at the provided address for the passed attributes * with a default message appended. Intended for use in providing a failure description for an operation * or an exception message for an {@link org.jboss.as.controller.OperationFailedException}. * The default appended message is 'Attributes are not understood in the target model version and this resource * will need to be ignored on the target host.' * * @param address where warning occurred * @param operation where which problem occurred * @param attributes attributes we that have problems about */ public String getAttributeWarning(PathAddress address, ModelNode operation, String... attributes) { return getAttributeWarning(address, operation, null, attributes); } /** * Get a warning message for the given operation at the provided address for the passed attributes * with a default message appended. This is useful when you need to pass it as result of getFailureMessage() * The default appended message is 'Attributes are not understood in the target model version and this resource * will need to be ignored on the target host.' * * @param address where warning occurred * @param operation where which problem occurred * @param attributes attributes we that have problems about */ public String getAttributeWarning(PathAddress address, ModelNode operation, Set<String> attributes) { return new AttributeLogEntry(address, operation, null, attributes).getMessage(); } public String getRejectedResourceWarning(PathAddress address, ModelNode operation) { return new RejectResourceLogEntry(address, operation).getMessage(); } public void logRejectedResourceWarning(PathAddress address, ModelNode operation) { messageQueue.add(new RejectResourceLogEntry(address, null)); } public void logDiscardedResourceWarning(PathAddress address, String host) { messageQueue.add(new LogEntry() { @Override public String getMessage() { return ControllerLogger.ROOT_LOGGER.discardedResourceTransformation(address, host); } }); } /** * Log a free-form warning * @param message the warning message. Cannot be {@code null} */ public void logWarning(final String message) { messageQueue.add(new LogEntry() { @Override public String getMessage() { return message; } }); } /** * flushes log queue, this actually writes combined log message into system log */ void flushLogQueue() { Set<String> problems = new LinkedHashSet<String>(); for (LogEntry entry : messageQueue) { problems.add("\t\t" + entry.getMessage() + "\n"); } if (!problems.isEmpty()) { logger.transformationWarnings(target.getHostName(), problems); } } private interface LogEntry { String getMessage(); } private class AttributeLogEntry implements LogEntry { private final PathAddress address; private final ModelNode operation; private final String message; private final Set<String> attributes; private AttributeLogEntry(PathAddress address, ModelNode operation, String message, String... attributes) { this(address, operation, message, new TreeSet<String>(Arrays.asList(attributes))); } private AttributeLogEntry(PathAddress address, ModelNode operation, String message, Set<String> attributes) { assert message != null || (attributes != null && attributes.size() > 0) : "a message must be provided or a list of attributes or both"; this.address = address; this.operation = operation; this.message = message; this.attributes = attributes; } public String getMessage() { final ModelVersion coreVersion = target.getVersion(); final String subsystemName = findSubsystemName(address); final ModelVersion usedVersion = subsystemName == null ? coreVersion : target.getSubsystemVersion(subsystemName); String msg = message == null ? ControllerLogger.ROOT_LOGGER.attributesAreNotUnderstoodAndMustBeIgnored() : message; String attributeSet = attributes != null && attributes.size() > 0 ? ControllerLogger.ROOT_LOGGER.attributeNames(attributes) : ""; if (operation == null) {//resource transformation if (subsystemName != null) { return ControllerLogger.ROOT_LOGGER.transformerLoggerSubsystemModelResourceTransformerAttributes(address, subsystemName, usedVersion, attributeSet, msg); } else { return ControllerLogger.ROOT_LOGGER.transformerLoggerCoreModelResourceTransformerAttributes(address, usedVersion, attributeSet, msg); } } else {//operation transformation if (subsystemName != null) { return ControllerLogger.ROOT_LOGGER.transformerLoggerSubsystemModelOperationTransformerAttributes(operation, address, subsystemName, usedVersion, attributeSet, msg); } else { return ControllerLogger.ROOT_LOGGER.transformerLoggerCoreModelOperationTransformerAttributes(operation, address, usedVersion, attributeSet, msg); } } } } private class RejectResourceLogEntry implements LogEntry { private final PathAddress address; private final ModelNode operation; private RejectResourceLogEntry(PathAddress address, ModelNode operation) { this.address = address; this.operation = operation; } @Override public String getMessage() { if (operation != null) { return ControllerLogger.ROOT_LOGGER.rejectResourceOperationTransformation(address, operation); } else { return ControllerLogger.ROOT_LOGGER.rejectedResourceResourceTransformation(address); } } } }