Skip to content

8351413: Remove XML interchange in java.management/javax/management/modelmbean/DescriptorSupport #25697

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -174,137 +174,6 @@ public DescriptorSupport(DescriptorSupport inDescr) {
init(inDescr.descriptorMap);
}


/**
* <p>Descriptor constructor taking an XML String.</p>
*
* <p>The format of the XML string is not defined, but an
* implementation must ensure that the string returned by
* {@link #toXMLString() toXMLString()} on an existing
* descriptor can be used to instantiate an equivalent
* descriptor using this constructor.</p>
*
* <p>In this implementation, all field values will be created
* as Strings. If the field values are not Strings, the
* programmer will have to reset or convert these fields
* correctly.</p>
*
* @param inStr An XML-formatted string used to populate this
* Descriptor. The format is not defined, but any
* implementation must ensure that the string returned by
* method {@link #toXMLString toXMLString} on an existing
* descriptor can be used to instantiate an equivalent
* descriptor when instantiated using this constructor.
*
* @exception RuntimeOperationsException If the String inStr
* passed in parameter is null
* @exception XMLParseException XML parsing problem while parsing
* the input String
* @exception MBeanException Wraps a distributed communication Exception.
* @deprecated This constructor exists for historical reasons. If
* reading from XML is required, it should be implemented externally.
*/
/* At some stage we should rewrite this code to be cleverer. Using
a StringTokenizer as we do means, first, that we accept a lot of
bogus strings without noticing they are bogus, and second, that we
split the string being parsed at characters like > even if they
occur in the middle of a field value. */
@Deprecated(since="25", forRemoval=true)
@SuppressWarnings("removal")
public DescriptorSupport(String inStr)
throws MBeanException, RuntimeOperationsException,
XMLParseException {
/* parse an XML-formatted string and populate internal
* structure with it */
if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) {
MODELMBEAN_LOGGER.log(Level.TRACE,
"Descriptor(String = '" + inStr + "') Constructor");
}
if (inStr == null) {
if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) {
MODELMBEAN_LOGGER.log(Level.TRACE,
"Descriptor(String = null) Illegal arguments");
}
final String msg = "String in parameter is null";
final RuntimeException iae = new IllegalArgumentException(msg);
throw new RuntimeOperationsException(iae, msg);
}

final String lowerInStr = inStr.toLowerCase(Locale.ENGLISH);
if (!lowerInStr.startsWith("<descriptor>")
|| !lowerInStr.endsWith("</descriptor>")) {
throw new XMLParseException("No <descriptor>, </descriptor> pair");
// XMLParseException is deprecated for removal.
}

// parse xmlstring into structures
init(null);
// create dummy descriptor: should have same size
// as number of fields in xmlstring
// loop through structures and put them in descriptor

StringTokenizer st = new StringTokenizer(inStr, "<> \t\n\r\f");

boolean inFld = false;
boolean inDesc = false;
String fieldName = null;
String fieldValue = null;


while (st.hasMoreTokens()) { // loop through tokens
String tok = st.nextToken();

if (tok.equalsIgnoreCase("FIELD")) {
inFld = true;
} else if (tok.equalsIgnoreCase("/FIELD")) {
if ((fieldName != null) && (fieldValue != null)) {
fieldName =
fieldName.substring(fieldName.indexOf('"') + 1,
fieldName.lastIndexOf('"'));
final Object fieldValueObject =
parseQuotedFieldValue(fieldValue);
setField(fieldName, fieldValueObject);
}
fieldName = null;
fieldValue = null;
inFld = false;
} else if (tok.equalsIgnoreCase("DESCRIPTOR")) {
inDesc = true;
} else if (tok.equalsIgnoreCase("/DESCRIPTOR")) {
inDesc = false;
fieldName = null;
fieldValue = null;
inFld = false;
} else if (inFld && inDesc) {
// want kw=value, eg, name="myname" value="myvalue"
int eq_separator = tok.indexOf('=');
if (eq_separator > 0) {
String kwPart = tok.substring(0,eq_separator);
String valPart = tok.substring(eq_separator+1);
if (kwPart.equalsIgnoreCase("NAME"))
fieldName = valPart;
else if (kwPart.equalsIgnoreCase("VALUE"))
fieldValue = valPart;
else { // xml parse exception
final String msg =
"Expected `name' or `value', got `" + tok + "'";
throw new XMLParseException(msg);
// XMLParseException is deprecated for removal.
}
} else { // xml parse exception
final String msg =
"Expected `keyword=value', got `" + tok + "'";
throw new XMLParseException(msg);
// XMLParseException is deprecated for removal.
}
}
} // while tokens
if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) {
MODELMBEAN_LOGGER.log(Level.TRACE,
"Descriptor(XMLString) Exit");
}
}

/**
* Constructor taking field names and field values. Neither array
* can be null.
Expand Down Expand Up @@ -946,231 +815,6 @@ private boolean validateField(String fldName, Object fldValue) {
return true;
}



/**
* <p>Returns an XML String representing the descriptor.</p>
*
* <p>The format is not defined, but an implementation must
* ensure that the string returned by this method can be
* used to build an equivalent descriptor when instantiated
* using the constructor {@link #DescriptorSupport(String)
* DescriptorSupport(String inStr)}.</p>
*
* <p>Fields which are not String objects will have toString()
* called on them to create the value. The value will be
* enclosed in parentheses. It is not guaranteed that you can
* reconstruct these objects unless they have been
* specifically set up to support toString() in a meaningful
* format and have a matching constructor that accepts a
* String in the same format.</p>
*
* <p>If the descriptor is empty the following String is
* returned: &lt;Descriptor&gt;&lt;/Descriptor&gt;</p>
*
* @return the XML string.
*
* @exception RuntimeOperationsException for illegal value for
* field Names or field Values. If the XML formatted string
* construction fails for any reason, this exception will be
* thrown.
* @deprecated This method exists for historical reasons. If
* writing to XML is required, it should be implemented externally.
*/
@Deprecated(since="25", forRemoval=true)
public synchronized String toXMLString() {
final StringBuilder buf = new StringBuilder("<Descriptor>");
Set<Map.Entry<String, Object>> returnedSet = descriptorMap.entrySet();
for (Map.Entry<String, Object> currElement : returnedSet) {
final String name = currElement.getKey();
Object value = currElement.getValue();
String valueString = null;
/* Set valueString to non-null if and only if this is a string that
cannot be confused with the encoding of an object. If it
could be so confused (surrounded by parentheses) then we
call makeFieldValue as for any non-String object and end
up with an encoding like "(java.lang.String/(thing))". */
if (value instanceof String) {
final String svalue = (String) value;
if (!svalue.startsWith("(") || !svalue.endsWith(")"))
valueString = quote(svalue);
}
if (valueString == null)
valueString = makeFieldValue(value);
buf.append("<field name=\"").append(name).append("\" value=\"")
.append(valueString).append("\"></field>");
}
buf.append("</Descriptor>");
return buf.toString();
}

private static final String[] entities = {
" &#32;",
"\"&quot;",
"<&lt;",
">&gt;",
"&&amp;",
"\r&#13;",
"\t&#9;",
"\n&#10;",
"\f&#12;",
};
private static final Map<String,Character> entityToCharMap =
new HashMap<>();
private static final String[] charToEntityMap;

static {
char maxChar = 0;
for (int i = 0; i < entities.length; i++) {
final char c = entities[i].charAt(0);
if (c > maxChar)
maxChar = c;
}
charToEntityMap = new String[maxChar + 1];
for (int i = 0; i < entities.length; i++) {
final char c = entities[i].charAt(0);
final String entity = entities[i].substring(1);
charToEntityMap[c] = entity;
entityToCharMap.put(entity, c);
}
}

private static boolean isMagic(char c) {
return (c < charToEntityMap.length && charToEntityMap[c] != null);
}

/*
* Quote the string so that it will be acceptable to the (String)
* constructor. Since the parsing code in that constructor is fairly
* stupid, we're obliged to quote apparently innocuous characters like
* space, <, and >. In a future version, we should rewrite the parser
* and only quote " plus either \ or & (depending on the quote syntax).
*/
private static String quote(String s) {
boolean found = false;
for (int i = 0; i < s.length(); i++) {
if (isMagic(s.charAt(i))) {
found = true;
break;
}
}
if (!found)
return s;
final StringBuilder buf = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (isMagic(c))
buf.append(charToEntityMap[c]);
else
buf.append(c);
}
return buf.toString();
}

@SuppressWarnings("removal")
private static String unquote(String s) throws XMLParseException {
if (!s.startsWith("\"") || !s.endsWith("\"")) {
throw new XMLParseException("Value must be quoted: <" + s + ">");
// XMLParseException is deprecated for removal.
}
final StringBuilder buf = new StringBuilder();
final int len = s.length() - 1;
for (int i = 1; i < len; i++) {
final char c = s.charAt(i);
final int semi;
final Character quoted;
if (c == '&'
&& (semi = s.indexOf(';', i + 1)) >= 0
&& ((quoted = entityToCharMap.get(s.substring(i, semi+1)))
!= null)) {
buf.append(quoted);
i = semi;
} else
buf.append(c);
}
return buf.toString();
}

/**
* Make the string that will go inside "..." for a value that is not
* a plain String.
* @throws RuntimeOperationsException if the value cannot be encoded.
*/
private static String makeFieldValue(Object value) {
if (value == null)
return "(null)";

Class<?> valueClass = value.getClass();
try {
valueClass.getConstructor(String.class);
} catch (NoSuchMethodException e) {
final String msg =
"Class " + valueClass + " does not have a public " +
"constructor with a single string arg";
final RuntimeException iae = new IllegalArgumentException(msg);
throw new RuntimeOperationsException(iae,
"Cannot make XML descriptor");
} catch (SecurityException e) {
// OK: we'll pretend the constructor is there
// too bad if it's not: we'll find out when we try to
// reconstruct the DescriptorSupport
}

final String quotedValueString = quote(value.toString());

return "(" + valueClass.getName() + "/" + quotedValueString + ")";
}

/*
* Parse a field value from the XML produced by toXMLString().
* Given a descriptor XML containing <field name="nnn" value="vvv">,
* the argument to this method will be "vvv" (a string including the
* containing quote characters). If vvv begins and ends with parentheses,
* then it may contain:
* - the characters "null", in which case the result is null;
* - a value of the form "some.class.name/xxx", in which case the
* result is equivalent to `new some.class.name("xxx")';
* - some other string, in which case the result is that string,
* without the parentheses.
*/
@SuppressWarnings("removal")
private static Object parseQuotedFieldValue(String s)
throws XMLParseException {
s = unquote(s);
if (s.equalsIgnoreCase("(null)"))
return null;
if (!s.startsWith("(") || !s.endsWith(")"))
return s;
final int slash = s.indexOf('/');
if (slash < 0) {
// compatibility: old code didn't include class name
return s.substring(1, s.length() - 1);
}
final String className = s.substring(1, slash);

final Constructor<?> constr;
try {
final ClassLoader contextClassLoader =
Thread.currentThread().getContextClassLoader();
final Class<?> c =
Class.forName(className, false, contextClassLoader);
constr = c.getConstructor(new Class<?>[] {String.class});
} catch (Exception e) {
throw new XMLParseException(e, "Cannot parse value: <" + s + ">");
// XMLParseException is deprecated for removal.
}
final String arg = s.substring(slash + 1, s.length() - 1);
try {
return constr.newInstance(new Object[] {arg});
} catch (Exception e) {
final String msg =
"Cannot construct instance of " + className +
" with arg: <" + s + ">";
throw new XMLParseException(e, msg);
// XMLParseException is deprecated for removal.
}
}

/**
* Returns a human readable string representing the
* descriptor. The string will be in the format of
Expand Down
Loading