/* * JBoss, Home of Professional Open Source. * Copyright 2011, Red Hat Middleware LLC, 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.jmx.model; import java.util.ArrayList; import java.util.HashMap; import java.util.Hashtable; import java.util.List; import java.util.Map; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.PathElement; import org.jboss.as.controller.registry.ImmutableManagementResourceRegistration; import org.jboss.as.controller.registry.Resource; import org.jboss.as.jmx.logging.JmxLogger; /** * Utility class to convert between PathAddress and ObjectName * * @author <a href="kabir.khan@jboss.com">Kabir Khan</a> */ class ObjectNameAddressUtil { private static final EscapedCharacter[] ESCAPED_KEY_CHARACTERS; static { List<EscapedCharacter> keys = new ArrayList<EscapedCharacter>(); //From ObjectName javadoc: //Each key is a nonempty string of characters which may not contain any of the characters //comma (,), equals (=), colon, asterisk, or question mark. The same key may not occur twice in a given ObjectName. keys.add(new EscapedCharacter('*')); keys.add(new EscapedCharacter('?')); keys.add(new EscapedCharacter(':')); keys.add(new EscapedCharacter('=')); keys.add(new EscapedCharacter(',')); ESCAPED_KEY_CHARACTERS = keys.toArray(new EscapedCharacter[keys.size()]); } static ObjectName createObjectName(final String domain, final PathAddress pathAddress) { if (pathAddress.size() == 0) { return ModelControllerMBeanHelper.createRootObjectName(domain); } final StringBuilder sb = new StringBuilder(domain); sb.append(":"); boolean first = true; for (PathElement element : pathAddress) { if (first) { first = false; } else { sb.append(","); } escapeKey(ESCAPED_KEY_CHARACTERS, sb, element.getKey()); sb.append("="); escapeValue(sb, element.getValue()); } try { return ObjectName.getInstance(sb.toString()); } catch (MalformedObjectNameException e) { throw JmxLogger.ROOT_LOGGER.cannotCreateObjectName(e, pathAddress, sb.toString()); } } /** * Converts the ObjectName to a PathAddress. * * @param name the ObjectName * * @return the PathAddress if it exists in the model, {@code null} otherwise */ static PathAddress resolvePathAddress(final String domain, final Resource rootResource, final ObjectName name) { if (!name.getDomain().equals(domain)) { return null; } if (name.equals(ModelControllerMBeanHelper.createRootObjectName(domain))) { return PathAddress.EMPTY_ADDRESS; } final Hashtable<String, String> properties = name.getKeyPropertyList(); return searchPathAddress(PathAddress.EMPTY_ADDRESS, rootResource, properties); } private static PathAddress searchPathAddress(final PathAddress address, final Resource resource, final Map<String, String> properties) { if (properties.size() == 0) { return address; } for (Map.Entry<String, String> entry : properties.entrySet()) { PathElement childElement = PathElement.pathElement( replaceEscapedCharactersInKey(entry.getKey()), replaceEscapedCharactersInValue(entry.getValue())); Resource child = resource.getChild(childElement); if (child != null) { Map<String, String> childProps = new HashMap<String, String>(properties); childProps.remove(entry.getKey()); PathAddress foundAddr = searchPathAddress(address.append(childElement), child, childProps); if (foundAddr != null) { return foundAddr; } } } return null; } /** * Straight conversion from an ObjectName to a PathAddress. * * There may not necessary be a Resource at this path address (if that correspond to a pattern) but it must * match a model in the registry. */ static PathAddress toPathAddress(String domain, ImmutableManagementResourceRegistration registry, ObjectName name) { if (!name.getDomain().equals(domain)) { return PathAddress.EMPTY_ADDRESS; } if (name.equals(ModelControllerMBeanHelper.createRootObjectName(domain))) { return PathAddress.EMPTY_ADDRESS; } final Hashtable<String, String> properties = name.getKeyPropertyList(); return searchPathAddress(PathAddress.EMPTY_ADDRESS, registry, properties); } private static PathAddress searchPathAddress(PathAddress address, ImmutableManagementResourceRegistration registry, Map<String, String> properties) { if (properties.size() == 0) { return address; } for (Map.Entry<String, String> entry : properties.entrySet()) { PathAddress childAddress = PathAddress.pathAddress( replaceEscapedCharactersInKey(entry.getKey()), replaceEscapedCharactersInValue(entry.getValue())); ImmutableManagementResourceRegistration subModel = registry.getSubModel(childAddress); if (subModel != null) { Map<String, String> childProps = new HashMap<String, String>(properties); childProps.remove(entry.getKey()); PathAddress foundAddr = searchPathAddress(address.append(childAddress), subModel, childProps); if (foundAddr != null) { return foundAddr; } } } return null; } private static void escapeKey(EscapedCharacter[] escapedCharacters, StringBuilder sb, String value) { for (EscapedCharacter escapedCharacter : escapedCharacters) { value = value.replace(escapedCharacter.getChar().toString(), escapedCharacter.getEscaped()); } sb.append(value); } private static void escapeValue(final StringBuilder sb, final String value) { final boolean containsAsterix = value.contains("*"); final boolean containsBackslash = value.contains("\\"); final boolean containsColon = value.contains(":"); final boolean containsEquals = value.contains("="); final boolean containsNewLine = value.contains("\n"); final boolean containsQuestionMark = value.contains("?"); final boolean containsQuote = value.contains("\""); final boolean containsComma = value.contains(","); boolean quoted = containsAsterix || containsBackslash || containsColon || containsEquals || containsNewLine || containsQuestionMark || containsQuote || containsComma; if (quoted) { String replaced = value; sb.append("\""); replaced = checkAndReplace(containsAsterix, replaced, "*", "\\*"); replaced = checkAndReplace(containsBackslash, replaced, "\\", "\\\\"); //colon, comma and equals do not need escaping replaced = checkAndReplace(containsNewLine, replaced, "\n", "\\n"); replaced = checkAndReplace(containsQuestionMark, replaced, "?", "\\?"); replaced = checkAndReplace(containsQuote, replaced, "\"", "\\\""); sb.append(replaced); sb.append("\""); } else { sb.append(value); } } private static String checkAndReplace(boolean condition, String original, String search, String replacement) { if (condition) { return original.replace(search, replacement); } return original; } private static String replaceEscapedCharactersInKey(String escaped) { if (escaped.contains("%x")) { for (EscapedCharacter escapedCharacter : ESCAPED_KEY_CHARACTERS) { escaped = escaped.replace(escapedCharacter.getEscaped(), escapedCharacter.getChar()); } } return escaped; } private static String replaceEscapedCharactersInValue(final String escaped) { if (escaped.startsWith("\"") && escaped.endsWith("\"")) { final boolean containsAsterix = escaped.contains("\\*"); final boolean containsBackslash = escaped.contains("\\\\"); final boolean containsNewLine = escaped.contains("\\n"); final boolean containsQuestionMark = escaped.contains("\\?"); final boolean containsQuote = escaped.contains("\\\""); String replaced = escaped.substring(1, escaped.length() - 1); replaced = checkAndReplace(containsAsterix, replaced, "\\*", "*"); replaced = checkAndReplace(containsBackslash, replaced, "\\\\", "\\"); replaced = checkAndReplace(containsNewLine, replaced, "\\n", "\n"); replaced = checkAndReplace(containsQuestionMark, replaced, "\\?", "?"); replaced = checkAndReplace(containsQuote, replaced, "\\\"", "\""); return replaced; } else { return escaped; } } private static class EscapedCharacter { private final String ch; private final String hexPart; private final String escaped; EscapedCharacter(Character ch){ this.ch = String.valueOf(ch); hexPart = Integer.toHexString(ch); escaped = "%x" + hexPart; } String getChar() { return ch; } String getEscaped() { return escaped; } } }