6602310: Extensions to Query API for JMX 2.0
6604768: IN queries require their arguments to be constants New JMX query language and support for dotted attributes in queries. Reviewed-by: dfuchs
This commit is contained in:
parent
1fd0bb2370
commit
104cc86359
@ -43,6 +43,13 @@ import javax.management.MBeanInfo;
|
||||
import javax.management.NotCompliantMBeanException;
|
||||
|
||||
import com.sun.jmx.mbeanserver.Util;
|
||||
import com.sun.jmx.remote.util.EnvHelp;
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import javax.management.AttributeNotFoundException;
|
||||
import javax.management.openmbean.CompositeData;
|
||||
|
||||
/**
|
||||
* This class contains the methods for performing all the tests needed to verify
|
||||
@ -482,4 +489,33 @@ public class Introspector {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Object elementFromComplex(Object complex, String element)
|
||||
throws AttributeNotFoundException {
|
||||
try {
|
||||
if (complex.getClass().isArray() && element.equals("length")) {
|
||||
return Array.getLength(complex);
|
||||
} else if (complex instanceof CompositeData) {
|
||||
return ((CompositeData) complex).get(element);
|
||||
} else {
|
||||
// Java Beans introspection
|
||||
//
|
||||
BeanInfo bi = java.beans.Introspector.getBeanInfo(complex.getClass());
|
||||
PropertyDescriptor[] pds = bi.getPropertyDescriptors();
|
||||
for (PropertyDescriptor pd : pds)
|
||||
if (pd.getName().equals(element))
|
||||
return pd.getReadMethod().invoke(complex);
|
||||
throw new AttributeNotFoundException(
|
||||
"Could not find the getter method for the property " +
|
||||
element + " using the Java Beans introspector");
|
||||
}
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
} catch (AttributeNotFoundException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw EnvHelp.initCause(
|
||||
new AttributeNotFoundException(e.getMessage()), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -104,4 +104,25 @@ class AndQueryExp extends QueryEval implements QueryExp {
|
||||
return "(" + exp1 + ") and (" + exp2 + ")";
|
||||
}
|
||||
|
||||
}
|
||||
@Override
|
||||
String toQueryString() {
|
||||
// Parentheses are only added if needed to disambiguate.
|
||||
return parens(exp1) + " and " + parens(exp2);
|
||||
}
|
||||
|
||||
// Add parens if needed to disambiguate an expression such as
|
||||
// Query.and(Query.or(a, b), c). We need to return
|
||||
// (a or b) and c
|
||||
// in such a case, because
|
||||
// a or b and c
|
||||
// would mean
|
||||
// a or (b and c)
|
||||
private static String parens(QueryExp exp) {
|
||||
String s = Query.toString(exp);
|
||||
if (exp instanceof OrQueryExp)
|
||||
return "(" + s + ")";
|
||||
else
|
||||
return s;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,12 +26,17 @@
|
||||
package javax.management;
|
||||
|
||||
|
||||
// RI import
|
||||
import javax.management.MBeanServer;
|
||||
import com.sun.jmx.mbeanserver.Introspector;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
|
||||
/**
|
||||
* Represents attributes used as arguments to relational constraints.
|
||||
* An <CODE>AttributeValueExp</CODE> may be used anywhere a <CODE>ValueExp</CODE> is required.
|
||||
* <p>Represents attributes used as arguments to relational constraints.
|
||||
* Instances of this class are usually obtained using {@link Query#attr(String)
|
||||
* Query.attr}.</p>
|
||||
*
|
||||
* <p>An <CODE>AttributeValueExp</CODE> may be used anywhere a
|
||||
* <CODE>ValueExp</CODE> is required.
|
||||
*
|
||||
* @since 1.5
|
||||
*/
|
||||
@ -46,6 +51,8 @@ public class AttributeValueExp implements ValueExp {
|
||||
*/
|
||||
private String attr;
|
||||
|
||||
private transient int dotIndex;
|
||||
|
||||
/**
|
||||
* An <code>AttributeValueExp</code> with a null attribute.
|
||||
* @deprecated An instance created with this constructor cannot be
|
||||
@ -64,6 +71,18 @@ public class AttributeValueExp implements ValueExp {
|
||||
*/
|
||||
public AttributeValueExp(String attr) {
|
||||
this.attr = attr;
|
||||
setDotIndex();
|
||||
}
|
||||
|
||||
private void setDotIndex() {
|
||||
if (attr != null)
|
||||
dotIndex = attr.indexOf('.');
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream in)
|
||||
throws ClassNotFoundException, IOException {
|
||||
in.defaultReadObject();
|
||||
setDotIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -76,7 +95,13 @@ public class AttributeValueExp implements ValueExp {
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the <CODE>AttributeValueExp</CODE> on an MBean.
|
||||
* <p>Applies the <CODE>AttributeValueExp</CODE> on an MBean.
|
||||
* This method calls {@link #getAttribute getAttribute(name)} and wraps
|
||||
* the result as a {@code ValueExp}. The value returned by
|
||||
* {@code getAttribute} must be a {@code Number}, {@code String},
|
||||
* or {@code Boolean}; otherwise this method throws a
|
||||
* {@code BadAttributeValueExpException}, which will cause
|
||||
* the containing query to be false for this {@code name}.</p>
|
||||
*
|
||||
* @param name The name of the MBean on which the <CODE>AttributeValueExp</CODE> will be applied.
|
||||
*
|
||||
@ -88,6 +113,7 @@ public class AttributeValueExp implements ValueExp {
|
||||
* @exception BadBinaryOpValueExpException
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public ValueExp apply(ObjectName name) throws BadStringOperationException, BadBinaryOpValueExpException,
|
||||
BadAttributeValueExpException, InvalidApplicationException {
|
||||
Object result = getAttribute(name);
|
||||
@ -106,8 +132,9 @@ public class AttributeValueExp implements ValueExp {
|
||||
/**
|
||||
* Returns the string representing its value.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return attr;
|
||||
return QueryParser.quoteId(attr);
|
||||
}
|
||||
|
||||
|
||||
@ -115,18 +142,38 @@ public class AttributeValueExp implements ValueExp {
|
||||
* Sets the MBean server on which the query is to be performed.
|
||||
*
|
||||
* @param s The MBean server on which the query is to be performed.
|
||||
*
|
||||
* @deprecated This method has no effect. The MBean Server used to
|
||||
* obtain an attribute value is {@link QueryEval#getMBeanServer()}.
|
||||
*/
|
||||
/* There is no need for this method, because if a query is being
|
||||
evaluted an AttributeValueExp can only appear inside a QueryExp,
|
||||
and that QueryExp will itself have done setMBeanServer. */
|
||||
@Deprecated
|
||||
@Override
|
||||
public void setMBeanServer(MBeanServer s) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the value of the given attribute in the named MBean.
|
||||
* <p>Return the value of the given attribute in the named MBean.
|
||||
* If the attempt to access the attribute generates an exception,
|
||||
* return null.
|
||||
* return null.</p>
|
||||
*
|
||||
* <p>Let <em>n</em> be the {@linkplain #getAttributeName attribute
|
||||
* name}. Then this method proceeds as follows. First it calls
|
||||
* {@link MBeanServer#getAttribute getAttribute(name, <em>n</em>)}. If that
|
||||
* generates an {@link AttributeNotFoundException}, and if <em>n</em>
|
||||
* contains at least one dot ({@code .}), then the method calls {@code
|
||||
* getAttribute(name, }<em>n</em>{@code .substring(0, }<em>n</em>{@code
|
||||
* .indexOf('.')))}; in other words it calls {@code getAttribute}
|
||||
* with the substring of <em>n</em> before the first dot. Then it
|
||||
* extracts a component from the retrieved value, as described in the <a
|
||||
* href="monitor/package-summary.html#complex">documentation for the {@code
|
||||
* monitor} package</a>.</p>
|
||||
*
|
||||
* <p>The MBean Server used is the one returned by {@link
|
||||
* QueryEval#getMBeanServer()}.</p>
|
||||
*
|
||||
* @param name the name of the MBean whose attribute is to be returned.
|
||||
*
|
||||
@ -139,10 +186,34 @@ public class AttributeValueExp implements ValueExp {
|
||||
|
||||
MBeanServer server = QueryEval.getMBeanServer();
|
||||
|
||||
try {
|
||||
return server.getAttribute(name, attr);
|
||||
} catch (AttributeNotFoundException e) {
|
||||
if (dotIndex < 0)
|
||||
throw e;
|
||||
}
|
||||
|
||||
String toGet = attr.substring(0, dotIndex);
|
||||
|
||||
Object value = server.getAttribute(name, toGet);
|
||||
|
||||
return extractElement(value, attr.substring(dotIndex + 1));
|
||||
} catch (Exception re) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Object extractElement(Object value, String elementWithDots)
|
||||
throws AttributeNotFoundException {
|
||||
while (true) {
|
||||
int dot = elementWithDots.indexOf('.');
|
||||
String element = (dot < 0) ?
|
||||
elementWithDots : elementWithDots.substring(0, dot);
|
||||
value = Introspector.elementFromComplex(value, element);
|
||||
if (dot < 0)
|
||||
return value;
|
||||
elementWithDots = elementWithDots.substring(dot + 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -109,34 +109,25 @@ class BetweenQueryExp extends QueryEval implements QueryExp {
|
||||
ValueExp val1 = exp1.apply(name);
|
||||
ValueExp val2 = exp2.apply(name);
|
||||
ValueExp val3 = exp3.apply(name);
|
||||
String sval1;
|
||||
String sval2;
|
||||
String sval3;
|
||||
double dval1;
|
||||
double dval2;
|
||||
double dval3;
|
||||
long lval1;
|
||||
long lval2;
|
||||
long lval3;
|
||||
boolean numeric = val1 instanceof NumericValueExp;
|
||||
|
||||
if (numeric) {
|
||||
if (((NumericValueExp)val1).isLong()) {
|
||||
lval1 = ((NumericValueExp)val1).longValue();
|
||||
lval2 = ((NumericValueExp)val2).longValue();
|
||||
lval3 = ((NumericValueExp)val3).longValue();
|
||||
long lval1 = ((NumericValueExp)val1).longValue();
|
||||
long lval2 = ((NumericValueExp)val2).longValue();
|
||||
long lval3 = ((NumericValueExp)val3).longValue();
|
||||
return lval2 <= lval1 && lval1 <= lval3;
|
||||
} else {
|
||||
dval1 = ((NumericValueExp)val1).doubleValue();
|
||||
dval2 = ((NumericValueExp)val2).doubleValue();
|
||||
dval3 = ((NumericValueExp)val3).doubleValue();
|
||||
double dval1 = ((NumericValueExp)val1).doubleValue();
|
||||
double dval2 = ((NumericValueExp)val2).doubleValue();
|
||||
double dval3 = ((NumericValueExp)val3).doubleValue();
|
||||
return dval2 <= dval1 && dval1 <= dval3;
|
||||
}
|
||||
|
||||
} else {
|
||||
sval1 = ((StringValueExp)val1).toString();
|
||||
sval2 = ((StringValueExp)val2).toString();
|
||||
sval3 = ((StringValueExp)val3).toString();
|
||||
String sval1 = ((StringValueExp)val1).getValue();
|
||||
String sval2 = ((StringValueExp)val2).getValue();
|
||||
String sval3 = ((StringValueExp)val3).getValue();
|
||||
return sval2.compareTo(sval1) <= 0 && sval1.compareTo(sval3) <= 0;
|
||||
}
|
||||
}
|
||||
@ -148,4 +139,8 @@ class BetweenQueryExp extends QueryEval implements QueryExp {
|
||||
return "(" + exp1 + ") between (" + exp2 + ") and (" + exp3 + ")";
|
||||
}
|
||||
|
||||
}
|
||||
@Override
|
||||
String toQueryString() {
|
||||
return exp1 + " between " + exp2 + " and " + exp3;
|
||||
}
|
||||
}
|
||||
|
@ -167,12 +167,74 @@ class BinaryOpValueExp extends QueryEval implements ValueExp {
|
||||
*/
|
||||
public String toString() {
|
||||
try {
|
||||
return exp1 + " " + opString() + " " + exp2;
|
||||
return parens(exp1, true) + " " + opString() + " " + parens(exp2, false);
|
||||
} catch (BadBinaryOpValueExpException ex) {
|
||||
return "invalid expression";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Add parentheses to the given subexpression if necessary to
|
||||
* preserve meaning. Suppose this BinaryOpValueExp is
|
||||
* Query.times(Query.plus(Query.attr("A"), Query.attr("B")), Query.attr("C")).
|
||||
* Then the original toString() logic would return A + B * C.
|
||||
* We check precedences in order to return (A + B) * C, which is the
|
||||
* meaning of the ValueExp.
|
||||
*
|
||||
* We need to add parentheses if the unparenthesized expression would
|
||||
* be parsed as a different ValueExp from the original.
|
||||
* We cannot omit parentheses even when mathematically
|
||||
* the result would be equivalent, because we do not know whether the
|
||||
* numeric values will be integer or floating-point. Addition and
|
||||
* multiplication are associative for integers but not always for
|
||||
* floating-point.
|
||||
*
|
||||
* So the rule is that we omit parentheses if the ValueExp
|
||||
* is (A op1 B) op2 C and the precedence of op1 is greater than or
|
||||
* equal to that of op2; or if the ValueExp is A op1 (B op2 C) and
|
||||
* the precedence of op2 is greater than that of op1. (There are two
|
||||
* precedences: that of * and / is greater than that of + and -.)
|
||||
* The case of (A op1 B) op2 (C op3 D) applies each rule in turn.
|
||||
*
|
||||
* The following examples show the rules in action. On the left,
|
||||
* the original ValueExp. On the right, the string representation.
|
||||
*
|
||||
* (A + B) + C A + B + C
|
||||
* (A * B) + C A * B + C
|
||||
* (A + B) * C (A + B) * C
|
||||
* (A * B) * C A * B * C
|
||||
* A + (B + C) A + (B + C)
|
||||
* A + (B * C) A + B * C
|
||||
* A * (B + C) A * (B + C)
|
||||
* A * (B * C) A * (B * C)
|
||||
*/
|
||||
private String parens(ValueExp subexp, boolean left)
|
||||
throws BadBinaryOpValueExpException {
|
||||
boolean omit;
|
||||
if (subexp instanceof BinaryOpValueExp) {
|
||||
int subop = ((BinaryOpValueExp) subexp).op;
|
||||
if (left)
|
||||
omit = (precedence(subop) >= precedence(op));
|
||||
else
|
||||
omit = (precedence(subop) > precedence(op));
|
||||
} else
|
||||
omit = true;
|
||||
|
||||
if (omit)
|
||||
return subexp.toString();
|
||||
else
|
||||
return "(" + subexp + ")";
|
||||
}
|
||||
|
||||
private int precedence(int xop) throws BadBinaryOpValueExpException {
|
||||
switch (xop) {
|
||||
case Query.PLUS: case Query.MINUS: return 0;
|
||||
case Query.TIMES: case Query.DIV: return 1;
|
||||
default:
|
||||
throw new BadBinaryOpValueExpException(this);
|
||||
}
|
||||
}
|
||||
|
||||
private String opString() throws BadBinaryOpValueExpException {
|
||||
switch (op) {
|
||||
case Query.PLUS:
|
||||
@ -188,4 +250,10 @@ class BinaryOpValueExp extends QueryEval implements ValueExp {
|
||||
throw new BadBinaryOpValueExpException(this);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setMBeanServer(MBeanServer s) {
|
||||
super.setMBeanServer(s);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -108,20 +108,12 @@ class BinaryRelQueryExp extends QueryEval implements QueryExp {
|
||||
BadAttributeValueExpException, InvalidApplicationException {
|
||||
Object val1 = exp1.apply(name);
|
||||
Object val2 = exp2.apply(name);
|
||||
String sval1;
|
||||
String sval2;
|
||||
double dval1;
|
||||
double dval2;
|
||||
long lval1;
|
||||
long lval2;
|
||||
boolean bval1;
|
||||
boolean bval2;
|
||||
boolean numeric = val1 instanceof NumericValueExp;
|
||||
boolean bool = val1 instanceof BooleanValueExp;
|
||||
if (numeric) {
|
||||
if (((NumericValueExp)val1).isLong()) {
|
||||
lval1 = ((NumericValueExp)val1).longValue();
|
||||
lval2 = ((NumericValueExp)val2).longValue();
|
||||
long lval1 = ((NumericValueExp)val1).longValue();
|
||||
long lval2 = ((NumericValueExp)val2).longValue();
|
||||
|
||||
switch (relOp) {
|
||||
case Query.GT:
|
||||
@ -136,8 +128,8 @@ class BinaryRelQueryExp extends QueryEval implements QueryExp {
|
||||
return lval1 == lval2;
|
||||
}
|
||||
} else {
|
||||
dval1 = ((NumericValueExp)val1).doubleValue();
|
||||
dval2 = ((NumericValueExp)val2).doubleValue();
|
||||
double dval1 = ((NumericValueExp)val1).doubleValue();
|
||||
double dval2 = ((NumericValueExp)val2).doubleValue();
|
||||
|
||||
switch (relOp) {
|
||||
case Query.GT:
|
||||
@ -155,8 +147,8 @@ class BinaryRelQueryExp extends QueryEval implements QueryExp {
|
||||
|
||||
} else if (bool) {
|
||||
|
||||
bval1 = ((BooleanValueExp)val1).getValue().booleanValue();
|
||||
bval2 = ((BooleanValueExp)val2).getValue().booleanValue();
|
||||
boolean bval1 = ((BooleanValueExp)val1).getValue().booleanValue();
|
||||
boolean bval2 = ((BooleanValueExp)val2).getValue().booleanValue();
|
||||
|
||||
switch (relOp) {
|
||||
case Query.GT:
|
||||
@ -172,8 +164,8 @@ class BinaryRelQueryExp extends QueryEval implements QueryExp {
|
||||
}
|
||||
|
||||
} else {
|
||||
sval1 = ((StringValueExp)val1).getValue();
|
||||
sval2 = ((StringValueExp)val2).getValue();
|
||||
String sval1 = ((StringValueExp)val1).getValue();
|
||||
String sval2 = ((StringValueExp)val2).getValue();
|
||||
|
||||
switch (relOp) {
|
||||
case Query.GT:
|
||||
@ -199,6 +191,11 @@ class BinaryRelQueryExp extends QueryEval implements QueryExp {
|
||||
return "(" + exp1 + ") " + relOpString() + " (" + exp2 + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
String toQueryString() {
|
||||
return exp1 + " " + relOpString() + " " + exp2;
|
||||
}
|
||||
|
||||
private String relOpString() {
|
||||
switch (relOp) {
|
||||
case Query.GT:
|
||||
|
@ -84,4 +84,10 @@ class BooleanValueExp extends QueryEval implements ValueExp {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setMBeanServer(MBeanServer s) {
|
||||
super.setMBeanServer(s);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -91,21 +91,23 @@ class InQueryExp extends QueryEval implements QueryExp {
|
||||
* @exception BadAttributeValueExpException
|
||||
* @exception InvalidApplicationException
|
||||
*/
|
||||
public boolean apply(ObjectName name) throws BadStringOperationException, BadBinaryOpValueExpException,
|
||||
public boolean apply(ObjectName name)
|
||||
throws BadStringOperationException, BadBinaryOpValueExpException,
|
||||
BadAttributeValueExpException, InvalidApplicationException {
|
||||
if (valueList != null) {
|
||||
ValueExp v = val.apply(name);
|
||||
boolean numeric = v instanceof NumericValueExp;
|
||||
|
||||
for (int i = 0; i < valueList.length; i++) {
|
||||
for (ValueExp element : valueList) {
|
||||
element = element.apply(name);
|
||||
if (numeric) {
|
||||
if (((NumericValueExp)valueList[i]).doubleValue() ==
|
||||
((NumericValueExp)v).doubleValue()) {
|
||||
if (((NumericValueExp) element).doubleValue() ==
|
||||
((NumericValueExp) v).doubleValue()) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (((StringValueExp)valueList[i]).getValue().equals(
|
||||
((StringValueExp)v).getValue())) {
|
||||
if (((StringValueExp) element).getValue().equals(
|
||||
((StringValueExp) v).getValue())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -113,7 +113,32 @@ class MatchQueryExp extends QueryEval implements QueryExp {
|
||||
}
|
||||
|
||||
private static String likeTranslate(String s) {
|
||||
return s.replace('?', '_').replace('*', '%');
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int c;
|
||||
for (int i = 0; i < s.length(); i += Character.charCount(c)) {
|
||||
c = s.codePointAt(i);
|
||||
switch (c) {
|
||||
case '\\':
|
||||
i += Character.charCount(c);
|
||||
sb.append('\\');
|
||||
if (i < s.length()) {
|
||||
c = s.codePointAt(i);
|
||||
sb.appendCodePoint(c);
|
||||
}
|
||||
break;
|
||||
case '*':
|
||||
sb.append('%'); break;
|
||||
case '?':
|
||||
sb.append('_'); break;
|
||||
case '%':
|
||||
sb.append("\\%"); break;
|
||||
case '_':
|
||||
sb.append("\\_"); break;
|
||||
default:
|
||||
sb.appendCodePoint(c); break;
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -86,8 +86,14 @@ class NotQueryExp extends QueryEval implements QueryExp {
|
||||
/**
|
||||
* Returns the string representing the object.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "not (" + exp + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
String toQueryString() {
|
||||
return "not (" + Query.toString(exp) + ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -151,11 +151,18 @@ class NumericValueExp extends QueryEval implements ValueExp {
|
||||
* Returns the string representing the object
|
||||
*/
|
||||
public String toString() {
|
||||
if (val == null)
|
||||
return "null";
|
||||
if (val instanceof Long || val instanceof Integer)
|
||||
{
|
||||
return String.valueOf(val.longValue());
|
||||
return Long.toString(val.longValue());
|
||||
}
|
||||
return String.valueOf(val.doubleValue());
|
||||
double d = val.doubleValue();
|
||||
if (Double.isInfinite(d))
|
||||
return (d > 0) ? "(1.0 / 0.0)" : "(-1.0 / 0.0)";
|
||||
if (Double.isNaN(d))
|
||||
return "(0.0 / 0.0)";
|
||||
return Double.toString(d);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -244,4 +251,10 @@ class NumericValueExp extends QueryEval implements ValueExp {
|
||||
out.defaultWriteObject();
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setMBeanServer(MBeanServer s) {
|
||||
super.setMBeanServer(s);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -222,7 +222,8 @@ import javax.management.QueryExp;
|
||||
* @since 1.5
|
||||
*/
|
||||
@SuppressWarnings("serial") // don't complain serialVersionUID not constant
|
||||
public class ObjectName implements Comparable<ObjectName>, QueryExp {
|
||||
public class ObjectName extends ToQueryString
|
||||
implements Comparable<ObjectName>, QueryExp {
|
||||
|
||||
/**
|
||||
* A structure recording property structure and
|
||||
@ -1779,10 +1780,16 @@ public class ObjectName implements Comparable<ObjectName>, QueryExp {
|
||||
*
|
||||
* @return a string representation of this object name.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return getSerializedNameString();
|
||||
}
|
||||
|
||||
@Override
|
||||
String toQueryString() {
|
||||
return "LIKE " + Query.value(toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the current object name with another object name. Two
|
||||
* ObjectName instances are equal if and only if their canonical
|
||||
|
@ -98,9 +98,29 @@ class OrQueryExp extends QueryEval implements QueryExp {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this AndQueryExp
|
||||
* Returns a string representation of this OrQueryExp
|
||||
*/
|
||||
public String toString() {
|
||||
return "(" + exp1 + ") or (" + exp2 + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
String toQueryString() {
|
||||
return parens(exp1) + " or " + parens(exp2);
|
||||
}
|
||||
|
||||
// Add parentheses to avoid possible confusion. If we have an expression
|
||||
// such as Query.or(Query.and(a, b), c), then we return
|
||||
// (a and b) or c
|
||||
// rather than just
|
||||
// a and b or c
|
||||
// In fact the precedence rules are such that the parentheses are not
|
||||
// strictly necessary, but omitting them would be confusing.
|
||||
private static String parens(QueryExp exp) {
|
||||
String s = Query.toString(exp);
|
||||
if (exp instanceof AndQueryExp)
|
||||
return "(" + s + ")";
|
||||
else
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
@ -27,9 +27,11 @@ package javax.management;
|
||||
|
||||
|
||||
/**
|
||||
* This class represents indexed attributes used as arguments to relational
|
||||
* constraints. An QualifiedAttributeValueExp may be used anywhere a
|
||||
* ValueExp is required.
|
||||
* <p>Represents attributes used as arguments to relational constraints,
|
||||
* where the attribute must be in an MBean of a specified {@linkplain
|
||||
* MBeanInfo#getClassName() class}. A QualifiedAttributeValueExp may be used
|
||||
* anywhere a ValueExp is required.
|
||||
*
|
||||
* @serial include
|
||||
*
|
||||
* @since 1.5
|
||||
@ -48,7 +50,9 @@ class QualifiedAttributeValueExp extends AttributeValueExp {
|
||||
|
||||
/**
|
||||
* Basic Constructor.
|
||||
* @deprecated see {@link AttributeValueExp#AttributeValueExp()}
|
||||
*/
|
||||
@Deprecated
|
||||
public QualifiedAttributeValueExp() {
|
||||
}
|
||||
|
||||
@ -81,6 +85,7 @@ class QualifiedAttributeValueExp extends AttributeValueExp {
|
||||
* @exception BadAttributeValueExpException
|
||||
* @exception InvalidApplicationException
|
||||
*/
|
||||
@Override
|
||||
public ValueExp apply(ObjectName name) throws BadStringOperationException, BadBinaryOpValueExpException,
|
||||
BadAttributeValueExpException, InvalidApplicationException {
|
||||
try {
|
||||
@ -105,9 +110,11 @@ class QualifiedAttributeValueExp extends AttributeValueExp {
|
||||
/**
|
||||
* Returns the string representing its value
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
if (className != null) {
|
||||
return className + "." + super.toString();
|
||||
return QueryParser.quoteId(className) + "#" +
|
||||
QueryParser.quoteId(super.toString());
|
||||
} else {
|
||||
return super.toString();
|
||||
}
|
||||
|
@ -27,19 +27,346 @@ package javax.management;
|
||||
|
||||
|
||||
/**
|
||||
* <p>Constructs query object constraints. The static methods provided
|
||||
* return query expressions that may be used in listing and
|
||||
* enumerating MBeans. Individual constraint construction methods
|
||||
* allow only appropriate types as arguments. Composition of calls can
|
||||
* construct arbitrary nestings of constraints, as the following
|
||||
* example illustrates:</p>
|
||||
* <p>Constructs query object constraints.</p>
|
||||
*
|
||||
* <p>The MBean Server can be queried for MBeans that meet a particular
|
||||
* condition, using its {@link MBeanServer#queryNames queryNames} or
|
||||
* {@link MBeanServer#queryMBeans queryMBeans} method. The {@link QueryExp}
|
||||
* parameter to the method can be any implementation of the interface
|
||||
* {@code QueryExp}, but it is usually best to obtain the {@code QueryExp}
|
||||
* value by calling the static methods in this class. This is particularly
|
||||
* true when querying a remote MBean Server: a custom implementation of the
|
||||
* {@code QueryExp} interface might not be present in the remote MBean Server,
|
||||
* but the methods in this class return only standard classes that are
|
||||
* part of the JMX implementation.</p>
|
||||
*
|
||||
* <p>There are two ways to create {@code QueryExp} objects using the methods
|
||||
* in this class. The first is to build them by chaining together calls to
|
||||
* the various methods. The second is to use the Query Language described
|
||||
* <a href="#ql">below</a> and produce the {@code QueryExp} by calling
|
||||
* {@link #fromString Query.fromString}. The two ways are equivalent:
|
||||
* every {@code QueryExp} returned by {@code fromString} can also be
|
||||
* constructed by chaining method calls.</p>
|
||||
*
|
||||
* <p>As an example, suppose you wanted to find all MBeans where the {@code
|
||||
* Enabled} attribute is {@code true} and the {@code Owner} attribute is {@code
|
||||
* "Duke"}. Here is how you could construct the appropriate {@code QueryExp} by
|
||||
* chaining together method calls:</p>
|
||||
*
|
||||
* <pre>
|
||||
* QueryExp exp = Query.and(Query.gt(Query.attr("age"),Query.value(5)),
|
||||
* Query.match(Query.attr("name"),
|
||||
* Query.value("Smith")));
|
||||
* QueryExp query =
|
||||
* Query.and(Query.eq(Query.attr("Enabled"), Query.value(true)),
|
||||
* Query.eq(Query.attr("Owner"), Query.value("Duke")));
|
||||
* </pre>
|
||||
*
|
||||
* <p>Here is how you could construct the same {@code QueryExp} using the
|
||||
* Query Language:</p>
|
||||
*
|
||||
* <pre>
|
||||
* QueryExp query = Query.fromString("Enabled = true and Owner = 'Duke'");
|
||||
* </pre>
|
||||
*
|
||||
* <p>The principal advantage of the method-chaining approach is that the
|
||||
* compiler will check that the query makes sense. The principal advantage
|
||||
* of the Query Language approach is that it is easier to write and especially
|
||||
* read.</p>
|
||||
*
|
||||
*
|
||||
* <h4 id="ql">Query Language</h4>
|
||||
*
|
||||
* <p>The query language is closely modeled on the WHERE clause of
|
||||
* SQL SELECT statements. The formal specification of the language
|
||||
* appears <a href="#formal-ql">below</a>, but it is probably easier to
|
||||
* understand it with examples such as the following.</p>
|
||||
*
|
||||
* <dl>
|
||||
* <dt>{@code Message = 'OK'}
|
||||
* <dd>Selects MBeans that have a {@code Message} attribute whose value
|
||||
* is the string {@code OK}.
|
||||
*
|
||||
* <dt>{@code FreeSpacePercent < 10}
|
||||
* <dd>Selects MBeans that have a {@code FreeSpacePercent} attribute whose
|
||||
* value is a number less than 10.
|
||||
*
|
||||
* <dt>{@code FreeSpacePercent < 10 and WarningSent = false}
|
||||
* <dd>Selects the same MBeans as the previous example, but they must
|
||||
* also have a boolean attribute {@code WarningSent} whose value
|
||||
* is false.
|
||||
*
|
||||
* <dt>{@code SpaceUsed > TotalSpace * (2.0 / 3.0)}
|
||||
* <dd>Selects MBeans that have {@code SpaceUsed} and {@code TotalSpace}
|
||||
* attributes where the first is more than two-thirds the second.
|
||||
*
|
||||
* <dt>{@code not (FreeSpacePercent between 10 and 90)}
|
||||
* <dd>Selects MBeans that have a {@code FreeSpacePercent} attribute whose
|
||||
* value is not between 10 and 90, inclusive.
|
||||
*
|
||||
* <dt>{@code FreeSpacePercent not between 10 and 90}
|
||||
* <dd>Another way of writing the previous query.
|
||||
*
|
||||
* <dt>{@code Status in ('STOPPED', 'STARTING', 'STARTED')}
|
||||
* <dd>Selects MBeans that have a {@code Status} attribute whose value
|
||||
* is one of those three strings.
|
||||
*
|
||||
* <dt>{@code Message like 'OK: %'}
|
||||
* <dd>Selects MBeans that have a {@code Message} attribute whose value
|
||||
* is a string beginning with {@code "OK: "}. <b>Notice that the
|
||||
* wildcard characters are SQL's ones.</b> In the query language,
|
||||
* {@code %} means "any sequence of characters" and {@code _}
|
||||
* means "any single character". In the rest of the JMX API, these
|
||||
* correspond to {@code *} and {@code %} respectively.
|
||||
*
|
||||
* <dt>{@code instanceof 'javax.management.NotificationBroadcaster'}
|
||||
* <dd>Selects MBeans that are instances of
|
||||
* {@link javax.management.NotificationBroadcaster}, as reported by
|
||||
* {@link javax.management.MBeanServer#isInstanceOf MBeanServer.isInstanceOf}.
|
||||
*
|
||||
* <dt>{@code like 'mydomain:*'}
|
||||
* <dd>Selects MBeans whose {@link ObjectName}s have the domain {@code mydomain}.
|
||||
*
|
||||
* </dl>
|
||||
*
|
||||
* <p>The last two examples do not correspond to valid SQL syntax, but all
|
||||
* the others do.</p>
|
||||
*
|
||||
* <p>The remainder of this description is a formal specification of the
|
||||
* query language.</p>
|
||||
*
|
||||
*
|
||||
* <h4 id="formal-ql">Lexical elements</h4>
|
||||
*
|
||||
* <p>Keywords such as <b>and</b>, <b>like</b>, and <b>between</b> are not
|
||||
* case sensitive. You can write <b>between</b>, <b>BETWEEN</b>, or
|
||||
* <b>BeTwEeN</b> with the same effect.</p>
|
||||
*
|
||||
* <p>On the other hand, attribute names <i>are</i> case sensitive. The
|
||||
* attribute {@code Name} is not the same as the attribute {@code name}.</p>
|
||||
*
|
||||
* <p>To access an attribute whose name, ignoring case, is the same as one of
|
||||
* the keywords {@code not}, {@code instanceof}, {@code like}, {@code true},
|
||||
* or {@code false}, you can use double quotes, for example {@code "not"}.
|
||||
* Double quotes can also be used to include non-identifier characters in
|
||||
* the name of an attribute, for example {@code "attribute-name-with-hyphens"}.
|
||||
* To include the double quote character in the attribute name, write it
|
||||
* twice. {@code "foo""bar""baz"} represents the attribute called
|
||||
* {@code foo"bar"baz}.
|
||||
*
|
||||
* <p>String constants are written with single quotes like {@code 'this'}. A
|
||||
* single quote within a string constant must be doubled, for example
|
||||
* {@code 'can''t'}.</p>
|
||||
*
|
||||
* <p>Integer constants are written as a sequence of decimal digits,
|
||||
* optionally preceded by a plus or minus sign. An integer constant must be
|
||||
* a valid input to {@link Long#valueOf(String)}.</p>
|
||||
*
|
||||
* <p>Floating-point constants are written using the Java syntax. A
|
||||
* floating-point constant must be a valid input to
|
||||
* {@link Double#valueOf(String)}.</p>
|
||||
*
|
||||
* <p>A boolean constant is either {@code true} or {@code false}, ignoring
|
||||
* case.</p>
|
||||
*
|
||||
* <p>Spaces cannot appear inside identifiers (unless written with double
|
||||
* quotes) or keywords or multi-character tokens such as {@code <=}. Spaces can
|
||||
* appear anywhere else, but are not required except to separate tokens. For
|
||||
* example, the query {@code a < b and 5 = c} could also be written {@code a<b
|
||||
* and 5=c}, but no further spaces can be removed.</p>
|
||||
*
|
||||
*
|
||||
* <h4 id="grammar-ql">Grammar</h4>
|
||||
*
|
||||
* <dl>
|
||||
* <dt id="query">query:
|
||||
* <dd><a href="#andquery">andquery</a> [<b>OR</b> <a href="#query">query</a>]
|
||||
*
|
||||
* <dt id="andquery">andquery:
|
||||
* <dd><a href="#predicate">predicate</a> [<b>AND</b> <a href="#andquery">andquery</a>]
|
||||
*
|
||||
* <dt id="predicate">predicate:
|
||||
* <dd><b>(</b> <a href="#query">query</a> <b>)</b> |<br>
|
||||
* <b>NOT</b> <a href="#predicate">predicate</a> |<br>
|
||||
* <b>INSTANCEOF</b> <a href="#stringvalue">stringvalue</a> |<br>
|
||||
* <b>LIKE</b> <a href="#objectnamepattern">objectnamepattern</a> |<br>
|
||||
* <a href="#value">value</a> <a href="#predrhs">predrhs</a>
|
||||
*
|
||||
* <dt id="predrhs">predrhs:
|
||||
* <dd><a href="#compare">compare</a> <a href="#value">value</a> |<br>
|
||||
* [<b>NOT</b>] <b>BETWEEN</b> <a href="#value">value</a> <b>AND</b>
|
||||
* <a href="#value">value</a> |<br>
|
||||
* [<b>NOT</b>] <b>IN (</b> <a href="#value">value</a>
|
||||
* <a href="#commavalues">commavalues</a> <b>)</b> |<br>
|
||||
* [<b>NOT</b>] <b>LIKE</b> <a href="#stringvalue">stringvalue</a>
|
||||
*
|
||||
* <dt id="commavalues">commavalues:
|
||||
* <dd>[ <b>,</b> <a href="#value">value</a> <a href="#commavalues">commavalues</a> ]
|
||||
*
|
||||
* <dt id="compare">compare:
|
||||
* <dd><b>=</b> | <b><</b> | <b>></b> |
|
||||
* <b><=</b> | <b>>=</b> | <b><></b> | <b>!=</b>
|
||||
*
|
||||
* <dt id="value">value:
|
||||
* <dd><a href="#factor">factor</a> [<a href="#plusorminus">plusorminus</a>
|
||||
* <a href="#value">value</a>]
|
||||
*
|
||||
* <dt id="plusorminus">plusorminus:
|
||||
* <dd><b>+</b> | <b>-</b>
|
||||
*
|
||||
* <dt id="factor">factor:
|
||||
* <dd><a href="#term">term</a> [<a href="#timesordivide">timesordivide</a>
|
||||
* <a href="#factor">factor</a>]
|
||||
*
|
||||
* <dt id="timesordivide">timesordivide:
|
||||
* <dd><b>*</b> | <b>/</b>
|
||||
*
|
||||
* <dt id="term">term:
|
||||
* <dd><a href="#attr">attr</a> | <a href="#literal">literal</a> |
|
||||
* <b>(</b> <a href="#value">value</a> <b>)</b>
|
||||
*
|
||||
* <dt id="attr">attr:
|
||||
* <dd><a href="#name">name</a> [<b>#</b> <a href="#name">name</a>]
|
||||
*
|
||||
* <dt id="name">name:
|
||||
* <dd><a href="#identifier">identifier</a> [<b>.</b><a href="#name">name</a>]
|
||||
*
|
||||
* <dt id="identifier">identifier:
|
||||
* <dd><i>Java-identifier</i> | <i>double-quoted-identifier</i>
|
||||
*
|
||||
* <dt id="literal">literal:
|
||||
* <dd><a href="#booleanlit">booleanlit</a> | <i>longlit</i> |
|
||||
* <i>doublelit</i> | <i>stringlit</i>
|
||||
*
|
||||
* <dt id="booleanlit">booleanlit:
|
||||
* <dd><b>FALSE</b> | <b>TRUE</b>
|
||||
*
|
||||
* <dt id="stringvalue">stringvalue:
|
||||
* <dd><i>stringlit</i>
|
||||
*
|
||||
* <dt id="objectnamepattern">objectnamepattern:
|
||||
* <dd><i>stringlit</i>
|
||||
*
|
||||
* </dl>
|
||||
*
|
||||
*
|
||||
* <h4>Semantics</h4>
|
||||
*
|
||||
* <p>The meaning of the grammar is described in the table below.
|
||||
* This defines a function <i>q</i> that maps a string to a Java object
|
||||
* such as a {@link QueryExp} or a {@link ValueExp}.</p>
|
||||
*
|
||||
* <table border="1" cellpadding="5">
|
||||
* <tr><th>String <i>s</i></th><th><i>q(s)</th></tr>
|
||||
*
|
||||
* <tr><td><i>query1</i> <b>OR</b> <i>query2</i>
|
||||
* <td>{@link Query#or Query.or}(<i>q(query1)</i>, <i>q(query2)</i>)
|
||||
*
|
||||
* <tr><td><i>query1</i> <b>AND</b> <i>query2</i>
|
||||
* <td>{@link Query#and Query.and}(<i>q(query1)</i>, <i>q(query2)</i>)
|
||||
*
|
||||
* <tr><td><b>(</b> <i>queryOrValue</i> <b>)</b>
|
||||
* <td><i>q(queryOrValue)</i>
|
||||
*
|
||||
* <tr><td><b>NOT</b> <i>query</i>
|
||||
* <td>{@link Query#not Query.not}(<i>q(query)</i>)
|
||||
*
|
||||
* <tr><td><b>INSTANCEOF</b> <i>stringLiteral</i>
|
||||
* <td>{@link Query#isInstanceOf Query.isInstanceOf}(<!--
|
||||
* -->{@link Query#value(String) Query.value}(<i>q(stringLiteral)</i>))
|
||||
*
|
||||
* <tr><td><b>LIKE</b> <i>stringLiteral</i>
|
||||
* <td>{@link ObjectName#ObjectName(String) new ObjectName}(<!--
|
||||
* --><i>q(stringLiteral)</i>)
|
||||
*
|
||||
* <tr><td><i>value1</i> <b>=</b> <i>value2</i>
|
||||
* <td>{@link Query#eq Query.eq}(<i>q(value1)</i>, <i>q(value2)</i>)
|
||||
*
|
||||
* <tr><td><i>value1</i> <b><</b> <i>value2</i>
|
||||
* <td>{@link Query#lt Query.lt}(<i>q(value1)</i>, <i>q(value2)</i>)
|
||||
*
|
||||
* <tr><td><i>value1</i> <b>></b> <i>value2</i>
|
||||
* <td>{@link Query#gt Query.gt}(<i>q(value1)</i>, <i>q(value2)</i>)
|
||||
*
|
||||
* <tr><td><i>value1</i> <b><=</b> <i>value2</i>
|
||||
* <td>{@link Query#leq Query.leq}(<i>q(value1)</i>, <i>q(value2)</i>)
|
||||
*
|
||||
* <tr><td><i>value1</i> <b>>=</b> <i>value2</i>
|
||||
* <td>{@link Query#geq Query.geq}(<i>q(value1)</i>, <i>q(value2)</i>)
|
||||
*
|
||||
* <tr><td><i>value1</i> <b><></b> <i>value2</i>
|
||||
* <td>{@link Query#not Query.not}({@link Query#eq Query.eq}(<!--
|
||||
* --><i>q(value1)</i>, <i>q(value2)</i>))
|
||||
*
|
||||
* <tr><td><i>value1</i> <b>!=</b> <i>value2</i>
|
||||
* <td>{@link Query#not Query.not}({@link Query#eq Query.eq}(<!--
|
||||
* --><i>q(value1)</i>, <i>q(value2)</i>))
|
||||
*
|
||||
* <tr><td><i>value1</i> <b>BETWEEN</b> <i>value2</i> AND <i>value3</i>
|
||||
* <td>{@link Query#between Query.between}(<i>q(value1)</i>,
|
||||
* <i>q(value2)</i>, <i>q(value3)</i>)
|
||||
*
|
||||
* <tr><td><i>value1</i> <b>NOT BETWEEN</b> <i>value2</i> AND <i>value3</i>
|
||||
* <td>{@link Query#not Query.not}({@link Query#between Query.between}(<!--
|
||||
* --><i>q(value1)</i>, <i>q(value2)</i>, <i>q(value3)</i>))
|
||||
*
|
||||
* <tr><td><i>value1</i> <b>IN (</b> <i>value2</i>, <i>value3</i> <b>)</b>
|
||||
* <td>{@link Query#in Query.in}(<i>q(value1)</i>,
|
||||
* <code>new ValueExp[] {</code>
|
||||
* <i>q(value2)</i>, <i>q(value3)</i><code>}</code>)
|
||||
*
|
||||
* <tr><td><i>value1</i> <b>NOT IN (</b> <i>value2</i>, <i>value3</i> <b>)</b>
|
||||
* <td>{@link Query#not Query.not}({@link Query#in Query.in}(<i>q(value1)</i>,
|
||||
* <code>new ValueExp[] {</code>
|
||||
* <i>q(value2)</i>, <i>q(value3)</i><code>}</code>))
|
||||
*
|
||||
* <tr><td><i>value</i> <b>LIKE</b> <i>stringLiteral</i>
|
||||
* <td>{@link Query#match Query.match}(<i>q(value)</i>,
|
||||
* <i><a href="#translateWildcards">translateWildcards</a>(q(stringLiteral))</i>)
|
||||
*
|
||||
* <tr><td><i>value</i> <b>NOT LIKE</b> <i>stringLiteral</i>
|
||||
* <td>{@link Query#not Query.not}({@link Query#match Query.match}(<i>q(value)</i>,
|
||||
* <i><a href="#translateWildcards">translateWildcards</a>(q(stringLiteral))</i>))
|
||||
*
|
||||
* <tr><td><i>value1</i> <b>+</b> <i>value2</i>
|
||||
* <td>{@link Query#plus Query.plus}(<i>q(value1)</i>, <i>q(value2)</i>)
|
||||
*
|
||||
* <tr><td><i>value1</i> <b>-</b> <i>value2</i>
|
||||
* <td>{@link Query#minus Query.minus}(<i>q(value1)</i>, <i>q(value2)</i>)
|
||||
*
|
||||
* <tr><td><i>value1</i> <b>*</b> <i>value2</i>
|
||||
* <td>{@link Query#times Query.times}(<i>q(value1)</i>, <i>q(value2)</i>)
|
||||
*
|
||||
* <tr><td><i>value1</i> <b>/</b> <i>value2</i>
|
||||
* <td>{@link Query#div Query.div}(<i>q(value1)</i>, <i>q(value2)</i>)
|
||||
*
|
||||
* <tr><td><i>name</i>
|
||||
* <td>{@link Query#attr(String) Query.attr}(<i>q(name)</i>)
|
||||
*
|
||||
* <tr><td><i>name1<b>#</b>name2</i>
|
||||
* <td>{@link Query#attr(String,String) Query.attr}(<i>q(name1)</i>,
|
||||
* <i>q(name2)</i>)
|
||||
*
|
||||
* <tr><td><b>FALSE</b>
|
||||
* <td>{@link Query#value(boolean) Query.value}(false)
|
||||
*
|
||||
* <tr><td><b>TRUE</b>
|
||||
* <td>{@link Query#value(boolean) Query.value}(true)
|
||||
*
|
||||
* <tr><td><i>decimalLiteral</i>
|
||||
* <td>{@link Query#value(long) Query.value}(<!--
|
||||
* -->{@link Long#valueOf(String) Long.valueOf}(<i>decimalLiteral</i>))
|
||||
*
|
||||
* <tr><td><i>floatingPointLiteral</i>
|
||||
* <td>{@link Query#value(double) Query.value}(<!--
|
||||
* -->{@link Double#valueOf(String) Double.valueOf}(<!--
|
||||
* --><i>floatingPointLiteral</i>))
|
||||
* </table>
|
||||
*
|
||||
* <p id="translateWildcards">Here, <i>translateWildcards</i> is a function
|
||||
* that translates from the SQL notation for wildcards, using {@code %} and
|
||||
* {@code _}, to the JMX API notation, using {@code *} and {@code ?}. If the
|
||||
* <b>LIKE</b> string already contains {@code *} or {@code ?}, these characters
|
||||
* have their literal meanings, and will be quoted in the call to
|
||||
* {@link Query#match Query.match}.</p>
|
||||
*
|
||||
* @since 1.5
|
||||
*/
|
||||
public class Query extends Object {
|
||||
@ -277,16 +604,12 @@ package javax.management;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns a new attribute expression.</p>
|
||||
*
|
||||
* <p>Evaluating this expression for a given
|
||||
* <code>objectName</code> includes performing {@link
|
||||
* MBeanServer#getAttribute MBeanServer.getAttribute(objectName,
|
||||
* name)}.</p>
|
||||
* <p>Returns a new attribute expression. See {@link AttributeValueExp}
|
||||
* for a detailed description of the semantics of the expression.</p>
|
||||
*
|
||||
* @param name The name of the attribute.
|
||||
*
|
||||
* @return An attribute expression for the attribute named name.
|
||||
* @return An attribute expression for the attribute named {@code name}.
|
||||
*/
|
||||
public static AttributeValueExp attr(String name) {
|
||||
return new AttributeValueExp(name);
|
||||
@ -627,6 +950,63 @@ package javax.management;
|
||||
return new InstanceOfQueryExp(classNameValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Return a string representation of the given query. The string
|
||||
* returned by this method can be converted back into an equivalent
|
||||
* query using {@link #fromString fromString}.</p>
|
||||
*
|
||||
* <p>(Two queries are equivalent if they produce the same result in
|
||||
* all cases. Equivalent queries are not necessarily identical:
|
||||
* for example the queries {@code Query.lt(Query.attr("A"), Query.attr("B"))}
|
||||
* and {@code Query.not(Query.ge(Query.attr("A"), Query.attr("B")))} are
|
||||
* equivalent but not identical.)</p>
|
||||
*
|
||||
* <p>The string returned by this method is only guaranteed to be converted
|
||||
* back into an equivalent query if {@code query} was constructed, or
|
||||
* could have been constructed, using the methods of this class.
|
||||
* If you make a custom query {@code myQuery} by implementing
|
||||
* {@link QueryExp} yourself then the result of
|
||||
* {@code Query.toString(myQuery)} is unspecified.</p>
|
||||
*
|
||||
* @param query the query to convert. If it is null, the result will
|
||||
* also be null.
|
||||
* @return the string representation of the query, or null if the
|
||||
* query is null.
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
public static String toString(QueryExp query) {
|
||||
if (query == null)
|
||||
return null;
|
||||
|
||||
if (query instanceof ToQueryString)
|
||||
return ((ToQueryString) query).toQueryString();
|
||||
|
||||
return query.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Produce a query from the given string. The query returned
|
||||
* by this method can be converted back into a string using
|
||||
* {@link #toString(QueryExp) toString}. The resultant string will
|
||||
* not necessarily be equal to {@code s}.</p>
|
||||
*
|
||||
* @param s the string to convert.
|
||||
*
|
||||
* @return a {@code QueryExp} derived by parsing the string, or
|
||||
* null if the string is null.
|
||||
*
|
||||
* @throws IllegalArgumentException if the string is not a valid
|
||||
* query string.
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
public static QueryExp fromString(String s) {
|
||||
if (s == null)
|
||||
return null;
|
||||
return new QueryParser(s).parseQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to escape strings used with
|
||||
* Query.{initial|any|final}SubString() methods.
|
||||
|
@ -38,7 +38,7 @@ import javax.management.MBeanServer;
|
||||
*
|
||||
* @since 1.5
|
||||
*/
|
||||
public abstract class QueryEval implements Serializable {
|
||||
public abstract class QueryEval extends ToQueryString implements Serializable {
|
||||
|
||||
/* Serial version */
|
||||
private static final long serialVersionUID = 2675899265640874796L;
|
||||
|
@ -30,9 +30,9 @@ import java.io.Serializable;
|
||||
|
||||
|
||||
/**
|
||||
* <p>Represents relational constraints that can be used in database
|
||||
* query "where clauses". Instances of QueryExp are returned by the
|
||||
* static methods of the {@link Query} class.</p>
|
||||
* <p>Represents relational constraints similar to database query "where
|
||||
* clauses". Instances of QueryExp are returned by the static methods of the
|
||||
* {@link Query} class.</p>
|
||||
*
|
||||
* <p>It is possible, but not
|
||||
* recommended, to create custom queries by implementing this
|
||||
@ -40,6 +40,7 @@ import java.io.Serializable;
|
||||
* QueryEval} class than to implement the interface directly, so that
|
||||
* the {@link #setMBeanServer} method works correctly.
|
||||
*
|
||||
* @see MBeanServer#queryNames MBeanServer.queryNames
|
||||
* @since 1.5
|
||||
*/
|
||||
public interface QueryExp extends Serializable {
|
||||
|
663
jdk/src/share/classes/javax/management/QueryParser.java
Normal file
663
jdk/src/share/classes/javax/management/QueryParser.java
Normal file
@ -0,0 +1,663 @@
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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 General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package javax.management;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Formatter;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* <p>Parser for JMX queries represented in an SQL-like language.</p>
|
||||
*/
|
||||
/*
|
||||
* Note that if a query starts with ( then we don't know whether it is
|
||||
* a predicate or just a value that is parenthesized. So, inefficiently,
|
||||
* we try to parse a predicate and if that doesn't work we try to parse
|
||||
* a value.
|
||||
*/
|
||||
class QueryParser {
|
||||
// LEXER STARTS HERE
|
||||
|
||||
private static class Token {
|
||||
final String string;
|
||||
Token(String s) {
|
||||
this.string = s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return string;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Token
|
||||
END = new Token("<end of string>"),
|
||||
LPAR = new Token("("), RPAR = new Token(")"),
|
||||
COMMA = new Token(","), DOT = new Token("."), SHARP = new Token("#"),
|
||||
PLUS = new Token("+"), MINUS = new Token("-"),
|
||||
TIMES = new Token("*"), DIVIDE = new Token("/"),
|
||||
LT = new Token("<"), GT = new Token(">"),
|
||||
LE = new Token("<="), GE = new Token(">="),
|
||||
NE = new Token("<>"), EQ = new Token("="),
|
||||
NOT = new Id("NOT"), INSTANCEOF = new Id("INSTANCEOF"),
|
||||
FALSE = new Id("FALSE"), TRUE = new Id("TRUE"),
|
||||
BETWEEN = new Id("BETWEEN"), AND = new Id("AND"),
|
||||
OR = new Id("OR"), IN = new Id("IN"),
|
||||
LIKE = new Id("LIKE"), CLASS = new Id("CLASS");
|
||||
|
||||
// Keywords that can appear where an identifier can appear.
|
||||
// If an attribute is one of these, then it must be quoted when
|
||||
// converting a query into a string.
|
||||
// We use a TreeSet so we can look up case-insensitively.
|
||||
private static final Set<String> idKeywords =
|
||||
new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
|
||||
static {
|
||||
for (Token t : new Token[] {NOT, INSTANCEOF, FALSE, TRUE, LIKE, CLASS})
|
||||
idKeywords.add(t.string);
|
||||
};
|
||||
|
||||
public static String quoteId(String id) {
|
||||
if (id.contains("\"") || idKeywords.contains(id))
|
||||
return '"' + id.replace("\"", "\"\"") + '"';
|
||||
else
|
||||
return id;
|
||||
}
|
||||
|
||||
private static class Id extends Token {
|
||||
Id(String id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
// All other tokens use object identity, which means e.g. that one
|
||||
// occurrence of the string constant 'x' is not the same as another.
|
||||
// For identifiers, we ignore case when testing for equality so that
|
||||
// for a keyword such as AND you can also spell it as "And" or "and".
|
||||
// But we keep the original case of the identifier, so if it's not
|
||||
// a keyword we will distinguish between the attribute Foo and the
|
||||
// attribute FOO.
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return (o instanceof Id && (((Id) o).toString().equalsIgnoreCase(toString())));
|
||||
}
|
||||
}
|
||||
|
||||
private static class QuotedId extends Token {
|
||||
QuotedId(String id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return '"' + string.replace("\"", "\"\"") + '"';
|
||||
}
|
||||
}
|
||||
|
||||
private static class StringLit extends Token {
|
||||
StringLit(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return '\'' + string.replace("'", "''") + '\'';
|
||||
}
|
||||
}
|
||||
|
||||
private static class LongLit extends Token {
|
||||
long number;
|
||||
|
||||
LongLit(long number) {
|
||||
super(Long.toString(number));
|
||||
this.number = number;
|
||||
}
|
||||
}
|
||||
|
||||
private static class DoubleLit extends Token {
|
||||
double number;
|
||||
|
||||
DoubleLit(double number) {
|
||||
super(Double.toString(number));
|
||||
this.number = number;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Tokenizer {
|
||||
private final String s;
|
||||
private final int len;
|
||||
private int i = 0;
|
||||
|
||||
Tokenizer(String s) {
|
||||
this.s = s;
|
||||
this.len = s.length();
|
||||
}
|
||||
|
||||
private int thisChar() {
|
||||
if (i == len)
|
||||
return -1;
|
||||
return s.codePointAt(i);
|
||||
}
|
||||
|
||||
private void advance() {
|
||||
i += Character.charCount(thisChar());
|
||||
}
|
||||
|
||||
private int thisCharAdvance() {
|
||||
int c = thisChar();
|
||||
advance();
|
||||
return c;
|
||||
}
|
||||
|
||||
Token nextToken() {
|
||||
// In this method, c is the character we're looking at, and
|
||||
// thisChar() is the character after that. Everything must
|
||||
// preserve these invariants. When we return we then have
|
||||
// thisChar() being the start of the following token, so
|
||||
// the next call to nextToken() will begin from there.
|
||||
int c;
|
||||
|
||||
// Skip space
|
||||
do {
|
||||
if (i == len)
|
||||
return null;
|
||||
c = thisCharAdvance();
|
||||
} while (Character.isWhitespace(c));
|
||||
|
||||
// Now c is the first character of the token, and tokenI points
|
||||
// to the character after that.
|
||||
switch (c) {
|
||||
case '(': return LPAR;
|
||||
case ')': return RPAR;
|
||||
case ',': return COMMA;
|
||||
case '.': return DOT;
|
||||
case '#': return SHARP;
|
||||
case '*': return TIMES;
|
||||
case '/': return DIVIDE;
|
||||
case '=': return EQ;
|
||||
case '-': return MINUS;
|
||||
case '+': return PLUS;
|
||||
|
||||
case '>':
|
||||
if (thisChar() == '=') {
|
||||
advance();
|
||||
return GE;
|
||||
} else
|
||||
return GT;
|
||||
|
||||
case '<':
|
||||
c = thisChar();
|
||||
switch (c) {
|
||||
case '=': advance(); return LE;
|
||||
case '>': advance(); return NE;
|
||||
default: return LT;
|
||||
}
|
||||
|
||||
case '!':
|
||||
if (thisCharAdvance() != '=')
|
||||
throw new IllegalArgumentException("'!' must be followed by '='");
|
||||
return NE;
|
||||
|
||||
case '"':
|
||||
case '\'': {
|
||||
int quote = c;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (true) {
|
||||
while ((c = thisChar()) != quote) {
|
||||
if (c < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unterminated string constant");
|
||||
}
|
||||
sb.appendCodePoint(thisCharAdvance());
|
||||
}
|
||||
advance();
|
||||
if (thisChar() == quote) {
|
||||
sb.appendCodePoint(quote);
|
||||
advance();
|
||||
} else
|
||||
break;
|
||||
}
|
||||
if (quote == '\'')
|
||||
return new StringLit(sb.toString());
|
||||
else
|
||||
return new QuotedId(sb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// Is it a numeric constant?
|
||||
if (Character.isDigit(c) || c == '.') {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int lastc = -1;
|
||||
while (true) {
|
||||
sb.appendCodePoint(c);
|
||||
c = Character.toLowerCase(thisChar());
|
||||
if (c == '+' || c == '-') {
|
||||
if (lastc != 'e')
|
||||
break;
|
||||
} else if (!Character.isDigit(c) && c != '.' && c != 'e')
|
||||
break;
|
||||
lastc = c;
|
||||
advance();
|
||||
}
|
||||
String s = sb.toString();
|
||||
if (s.indexOf('.') >= 0 || s.indexOf('e') >= 0) {
|
||||
double d = parseDoubleCheckOverflow(s);
|
||||
return new DoubleLit(d);
|
||||
} else {
|
||||
// Like the Java language, we allow the numeric constant
|
||||
// x where -x = Long.MIN_VALUE, even though x is not
|
||||
// representable as a long (it is Long.MAX_VALUE + 1).
|
||||
// Code in the parser will reject this value if it is
|
||||
// not the operand of unary minus.
|
||||
long l = -Long.parseLong("-" + s);
|
||||
return new LongLit(l);
|
||||
}
|
||||
}
|
||||
|
||||
// It must be an identifier.
|
||||
if (!Character.isJavaIdentifierStart(c)) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Formatter f = new Formatter(sb);
|
||||
f.format("Bad character: %c (%04x)", c, c);
|
||||
throw new IllegalArgumentException(sb.toString());
|
||||
}
|
||||
|
||||
StringBuilder id = new StringBuilder();
|
||||
while (true) { // identifier
|
||||
id.appendCodePoint(c);
|
||||
c = thisChar();
|
||||
if (!Character.isJavaIdentifierPart(c))
|
||||
break;
|
||||
advance();
|
||||
}
|
||||
|
||||
return new Id(id.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse a double as a Java compiler would do it, throwing an exception
|
||||
* if the input does not fit in a double. We assume that the input
|
||||
* string is not "Infinity" and does not have a leading sign.
|
||||
*/
|
||||
private static double parseDoubleCheckOverflow(String s) {
|
||||
double d = Double.parseDouble(s);
|
||||
if (Double.isInfinite(d))
|
||||
throw new NumberFormatException("Overflow: " + s);
|
||||
if (d == 0.0) { // Underflow checking is hard! CR 6604864
|
||||
String ss = s;
|
||||
int e = s.indexOf('e'); // we already forced E to lowercase
|
||||
if (e > 0)
|
||||
ss = s.substring(0, e);
|
||||
ss = ss.replace("0", "").replace(".", "");
|
||||
if (!ss.isEmpty())
|
||||
throw new NumberFormatException("Underflow: " + s);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
// PARSER STARTS HERE
|
||||
|
||||
private final List<Token> tokens;
|
||||
private int tokenI;
|
||||
// The current token is always tokens[tokenI].
|
||||
|
||||
QueryParser(String s) {
|
||||
// Construct the complete list of tokens immediately and append
|
||||
// a sentinel (END).
|
||||
tokens = new ArrayList<Token>();
|
||||
Tokenizer tokenizer = new Tokenizer(s);
|
||||
Token t;
|
||||
while ((t = tokenizer.nextToken()) != null)
|
||||
tokens.add(t);
|
||||
tokens.add(END);
|
||||
}
|
||||
|
||||
private Token current() {
|
||||
return tokens.get(tokenI);
|
||||
}
|
||||
|
||||
// If the current token is t, then skip it and return true.
|
||||
// Otherwise, return false.
|
||||
private boolean skip(Token t) {
|
||||
if (t.equals(current())) {
|
||||
tokenI++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the current token is one of the ones in 'tokens', then skip it
|
||||
// and return its index in 'tokens'. Otherwise, return -1.
|
||||
private int skipOne(Token... tokens) {
|
||||
for (int i = 0; i < tokens.length; i++) {
|
||||
if (skip(tokens[i]))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If the current token is t, then skip it and return.
|
||||
// Otherwise throw an exception.
|
||||
private void expect(Token t) {
|
||||
if (!skip(t))
|
||||
throw new IllegalArgumentException("Expected " + t + ", found " + current());
|
||||
}
|
||||
|
||||
private void next() {
|
||||
tokenI++;
|
||||
}
|
||||
|
||||
QueryExp parseQuery() {
|
||||
QueryExp qe = query();
|
||||
if (current() != END)
|
||||
throw new IllegalArgumentException("Junk at end of query: " + current());
|
||||
return qe;
|
||||
}
|
||||
|
||||
// The remainder of this class is a classical recursive-descent parser.
|
||||
// We only need to violate the recursive-descent scheme in one place,
|
||||
// where parentheses make the grammar not LL(1).
|
||||
|
||||
private QueryExp query() {
|
||||
QueryExp lhs = andquery();
|
||||
while (skip(OR))
|
||||
lhs = Query.or(lhs, andquery());
|
||||
return lhs;
|
||||
}
|
||||
|
||||
private QueryExp andquery() {
|
||||
QueryExp lhs = predicate();
|
||||
while (skip(AND))
|
||||
lhs = Query.and(lhs, predicate());
|
||||
return lhs;
|
||||
}
|
||||
|
||||
private QueryExp predicate() {
|
||||
// Grammar hack. If we see a paren, it might be (query) or
|
||||
// it might be (value). We try to parse (query), and if that
|
||||
// fails, we parse (value). For example, if the string is
|
||||
// "(2+3)*4 < 5" then we will try to parse the query
|
||||
// "2+3)*4 < 5", which will fail at the ), so we'll back up to
|
||||
// the paren and let value() handle it.
|
||||
if (skip(LPAR)) {
|
||||
int parenIndex = tokenI - 1;
|
||||
try {
|
||||
QueryExp qe = query();
|
||||
expect(RPAR);
|
||||
return qe;
|
||||
} catch (IllegalArgumentException e) {
|
||||
// OK: try parsing a value
|
||||
}
|
||||
tokenI = parenIndex;
|
||||
}
|
||||
|
||||
if (skip(NOT))
|
||||
return Query.not(predicate());
|
||||
|
||||
if (skip(INSTANCEOF))
|
||||
return Query.isInstanceOf(stringvalue());
|
||||
|
||||
if (skip(LIKE)) {
|
||||
StringValueExp sve = stringvalue();
|
||||
String s = sve.getValue();
|
||||
try {
|
||||
return new ObjectName(s);
|
||||
} catch (MalformedObjectNameException e) {
|
||||
throw new IllegalArgumentException(
|
||||
"Bad ObjectName pattern after LIKE: '" + s + "'", e);
|
||||
}
|
||||
}
|
||||
|
||||
ValueExp lhs = value();
|
||||
|
||||
return predrhs(lhs);
|
||||
}
|
||||
|
||||
// The order of elements in the following arrays is important. The code
|
||||
// in predrhs depends on integer indexes. Change with caution.
|
||||
private static final Token[] relations = {
|
||||
EQ, LT, GT, LE, GE, NE,
|
||||
// 0, 1, 2, 3, 4, 5,
|
||||
};
|
||||
private static final Token[] betweenLikeIn = {
|
||||
BETWEEN, LIKE, IN
|
||||
// 0, 1, 2,
|
||||
};
|
||||
|
||||
private QueryExp predrhs(ValueExp lhs) {
|
||||
Token start = current(); // for errors
|
||||
|
||||
// Look for < > = etc
|
||||
int i = skipOne(relations);
|
||||
if (i >= 0) {
|
||||
ValueExp rhs = value();
|
||||
switch (i) {
|
||||
case 0: return Query.eq(lhs, rhs);
|
||||
case 1: return Query.lt(lhs, rhs);
|
||||
case 2: return Query.gt(lhs, rhs);
|
||||
case 3: return Query.leq(lhs, rhs);
|
||||
case 4: return Query.geq(lhs, rhs);
|
||||
case 5: return Query.not(Query.eq(lhs, rhs));
|
||||
// There is no Query.ne so <> is shorthand for the above.
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
// Must be BETWEEN LIKE or IN, optionally preceded by NOT
|
||||
boolean not = skip(NOT);
|
||||
i = skipOne(betweenLikeIn);
|
||||
if (i < 0)
|
||||
throw new IllegalArgumentException("Expected relation at " + start);
|
||||
|
||||
QueryExp q;
|
||||
switch (i) {
|
||||
case 0: { // BETWEEN
|
||||
ValueExp lower = value();
|
||||
expect(AND);
|
||||
ValueExp upper = value();
|
||||
q = Query.between(lhs, lower, upper);
|
||||
break;
|
||||
}
|
||||
|
||||
case 1: { // LIKE
|
||||
if (!(lhs instanceof AttributeValueExp)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Left-hand side of LIKE must be an attribute");
|
||||
}
|
||||
AttributeValueExp alhs = (AttributeValueExp) lhs;
|
||||
StringValueExp sve = stringvalue();
|
||||
String s = sve.getValue();
|
||||
q = Query.match(alhs, patternValueExp(s));
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: { // IN
|
||||
expect(LPAR);
|
||||
List<ValueExp> values = new ArrayList<ValueExp>();
|
||||
values.add(value());
|
||||
while (skip(COMMA))
|
||||
values.add(value());
|
||||
expect(RPAR);
|
||||
q = Query.in(lhs, values.toArray(new ValueExp[values.size()]));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
if (not)
|
||||
q = Query.not(q);
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
private ValueExp value() {
|
||||
ValueExp lhs = factor();
|
||||
int i;
|
||||
while ((i = skipOne(PLUS, MINUS)) >= 0) {
|
||||
ValueExp rhs = factor();
|
||||
if (i == 0)
|
||||
lhs = Query.plus(lhs, rhs);
|
||||
else
|
||||
lhs = Query.minus(lhs, rhs);
|
||||
}
|
||||
return lhs;
|
||||
}
|
||||
|
||||
private ValueExp factor() {
|
||||
ValueExp lhs = term();
|
||||
int i;
|
||||
while ((i = skipOne(TIMES, DIVIDE)) >= 0) {
|
||||
ValueExp rhs = term();
|
||||
if (i == 0)
|
||||
lhs = Query.times(lhs, rhs);
|
||||
else
|
||||
lhs = Query.div(lhs, rhs);
|
||||
}
|
||||
return lhs;
|
||||
}
|
||||
|
||||
private ValueExp term() {
|
||||
boolean signed = false;
|
||||
int sign = +1;
|
||||
if (skip(PLUS))
|
||||
signed = true;
|
||||
else if (skip(MINUS)) {
|
||||
signed = true; sign = -1;
|
||||
}
|
||||
|
||||
Token t = current();
|
||||
next();
|
||||
|
||||
if (t instanceof DoubleLit)
|
||||
return Query.value(sign * ((DoubleLit) t).number);
|
||||
if (t instanceof LongLit) {
|
||||
long n = ((LongLit) t).number;
|
||||
if (n == Long.MIN_VALUE && sign != -1)
|
||||
throw new IllegalArgumentException("Illegal positive integer: " + n);
|
||||
return Query.value(sign * n);
|
||||
}
|
||||
if (signed)
|
||||
throw new IllegalArgumentException("Expected number after + or -");
|
||||
|
||||
if (t == LPAR) {
|
||||
ValueExp v = value();
|
||||
expect(RPAR);
|
||||
return v;
|
||||
}
|
||||
if (t.equals(FALSE) || t.equals(TRUE)) {
|
||||
return Query.value(t.equals(TRUE));
|
||||
}
|
||||
if (t.equals(CLASS))
|
||||
return Query.classattr();
|
||||
|
||||
if (t instanceof StringLit)
|
||||
return Query.value(t.string); // Not toString(), which would requote '
|
||||
|
||||
// At this point, all that remains is something that will call Query.attr
|
||||
|
||||
if (!(t instanceof Id) && !(t instanceof QuotedId))
|
||||
throw new IllegalArgumentException("Unexpected token " + t);
|
||||
|
||||
String name1 = name(t);
|
||||
|
||||
if (skip(SHARP)) {
|
||||
Token t2 = current();
|
||||
next();
|
||||
String name2 = name(t2);
|
||||
return Query.attr(name1, name2);
|
||||
}
|
||||
return Query.attr(name1);
|
||||
}
|
||||
|
||||
// Initially, t is the first token of a supposed name and current()
|
||||
// is the second.
|
||||
private String name(Token t) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (true) {
|
||||
if (!(t instanceof Id) && !(t instanceof QuotedId))
|
||||
throw new IllegalArgumentException("Unexpected token " + t);
|
||||
sb.append(t.string);
|
||||
if (current() != DOT)
|
||||
break;
|
||||
sb.append('.');
|
||||
next();
|
||||
t = current();
|
||||
next();
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private StringValueExp stringvalue() {
|
||||
// Currently the only way to get a StringValueExp when constructing
|
||||
// a QueryExp is via Query.value(String), so we only recognize
|
||||
// string literals here. But if we expand queries in the future
|
||||
// that might no longer be true.
|
||||
Token t = current();
|
||||
next();
|
||||
if (!(t instanceof StringLit))
|
||||
throw new IllegalArgumentException("Expected string: " + t);
|
||||
return Query.value(t.string);
|
||||
}
|
||||
|
||||
// Convert the SQL pattern syntax, using % and _, to the Query.match
|
||||
// syntax, using * and ?. The tricky part is recognizing \% and
|
||||
// \_ as literal values, and also not replacing them inside [].
|
||||
// But Query.match does not recognize \ inside [], which makes our
|
||||
// job a tad easier.
|
||||
private StringValueExp patternValueExp(String s) {
|
||||
int c;
|
||||
for (int i = 0; i < s.length(); i += Character.charCount(c)) {
|
||||
c = s.codePointAt(i);
|
||||
switch (c) {
|
||||
case '\\':
|
||||
i++; // i += Character.charCount(c), but we know it's 1!
|
||||
if (i >= s.length())
|
||||
throw new IllegalArgumentException("\\ at end of pattern");
|
||||
break;
|
||||
case '[':
|
||||
i = s.indexOf(']', i);
|
||||
if (i < 0)
|
||||
throw new IllegalArgumentException("[ without ]");
|
||||
break;
|
||||
case '%':
|
||||
s = s.substring(0, i) + "*" + s.substring(i + 1);
|
||||
break;
|
||||
case '_':
|
||||
s = s.substring(0, i) + "?" + s.substring(i + 1);
|
||||
break;
|
||||
case '*':
|
||||
case '?':
|
||||
s = s.substring(0, i) + '\\' + (char) c + s.substring(i + 1);
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Query.value(s);
|
||||
}
|
||||
}
|
@ -73,7 +73,7 @@ public class StringValueExp implements ValueExp {
|
||||
* Returns the string representing the object.
|
||||
*/
|
||||
public String toString() {
|
||||
return "'" + val + "'";
|
||||
return "'" + val.replace("'", "''") + "'";
|
||||
}
|
||||
|
||||
|
||||
|
38
jdk/src/share/classes/javax/management/ToQueryString.java
Normal file
38
jdk/src/share/classes/javax/management/ToQueryString.java
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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 General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package javax.management;
|
||||
|
||||
/* QueryExp classes can extend this to get non-default treatment for
|
||||
* Query.toString(q). We're reluctant to change the public toString()
|
||||
* methods of the classes because people might be parsing them, even
|
||||
* though that's rather fragile. But Query.toString(q) has no such
|
||||
* constraint so it can use the new toQueryString() method defined here.
|
||||
*/
|
||||
class ToQueryString {
|
||||
String toQueryString() {
|
||||
return toString();
|
||||
}
|
||||
}
|
@ -27,13 +27,8 @@ package javax.management.monitor;
|
||||
|
||||
import static com.sun.jmx.defaults.JmxProperties.MONITOR_LOGGER;
|
||||
import com.sun.jmx.mbeanserver.GetPropertyAction;
|
||||
import com.sun.jmx.remote.util.EnvHelp;
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import com.sun.jmx.mbeanserver.Introspector;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
@ -64,7 +59,6 @@ import javax.management.NotificationBroadcasterSupport;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.ReflectionException;
|
||||
import static javax.management.monitor.MonitorNotification.*;
|
||||
import javax.management.openmbean.CompositeData;
|
||||
|
||||
/**
|
||||
* Defines the part common to all monitor MBeans.
|
||||
@ -876,44 +870,13 @@ public abstract class Monitor
|
||||
if (isComplexTypeAttribute) {
|
||||
Object v = value;
|
||||
for (String attr : remainingAttributes)
|
||||
v = introspect(object, attr, v);
|
||||
v = Introspector.elementFromComplex(v, attr);
|
||||
return (Comparable<?>) v;
|
||||
} else {
|
||||
return (Comparable<?>) value;
|
||||
}
|
||||
}
|
||||
|
||||
Object introspect(ObjectName object,
|
||||
String attribute,
|
||||
Object value)
|
||||
throws AttributeNotFoundException {
|
||||
try {
|
||||
if (value.getClass().isArray() && attribute.equals("length")) {
|
||||
return Array.getLength(value);
|
||||
} else if (value instanceof CompositeData) {
|
||||
return ((CompositeData) value).get(attribute);
|
||||
} else {
|
||||
// Java Beans introspection
|
||||
//
|
||||
BeanInfo bi = Introspector.getBeanInfo(value.getClass());
|
||||
PropertyDescriptor[] pds = bi.getPropertyDescriptors();
|
||||
for (PropertyDescriptor pd : pds)
|
||||
if (pd.getName().equals(attribute))
|
||||
return pd.getReadMethod().invoke(value);
|
||||
throw new AttributeNotFoundException(
|
||||
"Could not find the getter method for the property " +
|
||||
attribute + " using the Java Beans introspector");
|
||||
}
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
} catch (AttributeNotFoundException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw EnvHelp.initCause(
|
||||
new AttributeNotFoundException(e.getMessage()), e);
|
||||
}
|
||||
}
|
||||
|
||||
boolean isComparableTypeValid(ObjectName object,
|
||||
String attribute,
|
||||
Comparable<?> value) {
|
||||
|
192
jdk/test/javax/management/query/QueryDottedAttrTest.java
Normal file
192
jdk/test/javax/management/query/QueryDottedAttrTest.java
Normal file
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code 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 General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test QueryDottedAttrTest
|
||||
* @bug 6602310
|
||||
* @summary Test that Query.attr can understand a.b etc.
|
||||
* @author Eamonn McManus
|
||||
*/
|
||||
|
||||
import java.beans.ConstructorProperties;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import javax.management.AttributeNotFoundException;
|
||||
import javax.management.MBeanException;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerFactory;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.Query;
|
||||
import javax.management.QueryExp;
|
||||
import javax.management.ReflectionException;
|
||||
import javax.management.StandardMBean;
|
||||
|
||||
public class QueryDottedAttrTest {
|
||||
public static class Complex {
|
||||
private final double re, im;
|
||||
|
||||
@ConstructorProperties({"real", "imaginary"})
|
||||
public Complex(double re, double im) {
|
||||
this.re = re;
|
||||
this.im = im;
|
||||
}
|
||||
|
||||
public double getRe() {
|
||||
return re;
|
||||
}
|
||||
|
||||
public double getIm() {
|
||||
return im;
|
||||
}
|
||||
}
|
||||
|
||||
public static interface Intf {
|
||||
Complex getComplex();
|
||||
int[] getIntArray();
|
||||
String[] getStringArray();
|
||||
}
|
||||
|
||||
public static class Impl implements Intf {
|
||||
public Complex getComplex() {
|
||||
return new Complex(1.0, 1.0);
|
||||
}
|
||||
|
||||
public int[] getIntArray() {
|
||||
return new int[] {1, 2, 3};
|
||||
}
|
||||
|
||||
public String[] getStringArray() {
|
||||
return new String[] {"one", "two", "three"};
|
||||
}
|
||||
}
|
||||
|
||||
public static interface TestMBean extends Intf {}
|
||||
|
||||
public static class Test extends Impl implements TestMBean {}
|
||||
|
||||
public static interface TestMXBean extends Intf {}
|
||||
|
||||
public static class TestMX extends Impl implements TestMXBean {}
|
||||
|
||||
public static class AttrWithDot extends StandardMBean {
|
||||
public <T> AttrWithDot(Object impl, Class<T> intf) {
|
||||
super(intf.cast(impl), intf, (intf == TestMXBean.class));
|
||||
}
|
||||
|
||||
public Object getAttribute(String attribute)
|
||||
throws AttributeNotFoundException, MBeanException, ReflectionException {
|
||||
if (attribute.equals("Complex.re"))
|
||||
return 2.0;
|
||||
else
|
||||
return super.getAttribute(attribute);
|
||||
}
|
||||
}
|
||||
|
||||
private static final boolean[] booleans = {false, true};
|
||||
|
||||
private static final QueryExp[] alwaysTrueQueries = {
|
||||
Query.eq(Query.attr("IntArray.length"), Query.value(3)),
|
||||
Query.eq(Query.attr("StringArray.length"), Query.value(3)),
|
||||
Query.eq(Query.attr("Complex.im"), Query.value(1.0)),
|
||||
};
|
||||
|
||||
private static final QueryExp[] alwaysFalseQueries = {
|
||||
Query.eq(Query.attr("IntArray.length"), Query.value("3")),
|
||||
Query.eq(Query.attr("IntArray.length"), Query.value(2)),
|
||||
Query.eq(Query.attr("Complex.im"), Query.value(-1.0)),
|
||||
Query.eq(Query.attr("Complex.xxx"), Query.value(0)),
|
||||
};
|
||||
|
||||
private static final QueryExp[] attrWithDotTrueQueries = {
|
||||
Query.eq(Query.attr("Complex.re"), Query.value(2.0)),
|
||||
};
|
||||
|
||||
private static final QueryExp[] attrWithDotFalseQueries = {
|
||||
Query.eq(Query.attr("Complex.re"), Query.value(1.0)),
|
||||
};
|
||||
|
||||
private static String failure;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
ObjectName name = new ObjectName("a:b=c");
|
||||
for (boolean attrWithDot : booleans) {
|
||||
for (boolean mx : booleans) {
|
||||
String what =
|
||||
(mx ? "MXBean" : "Standard MBean") +
|
||||
(attrWithDot ? " having attribute with dot in its name" : "");
|
||||
System.out.println("Testing " + what);
|
||||
Class<?> intf = mx ? TestMXBean.class : TestMBean.class;
|
||||
Object impl = mx ? new TestMX() : new Test();
|
||||
if (attrWithDot)
|
||||
impl = new AttrWithDot(impl, intf);
|
||||
MBeanServer mbs = MBeanServerFactory.newMBeanServer();
|
||||
mbs.registerMBean(impl, name);
|
||||
boolean ismx = "true".equals(
|
||||
mbs.getMBeanInfo(name).getDescriptor().getFieldValue("mxbean"));
|
||||
if (mx != ismx)
|
||||
fail("MBean should " + (mx ? "" : "not ") + "be MXBean");
|
||||
test(mbs, name, alwaysTrueQueries, true);
|
||||
test(mbs, name, alwaysFalseQueries, false);
|
||||
test(mbs, name, attrWithDotTrueQueries, attrWithDot);
|
||||
test(mbs, name, attrWithDotFalseQueries, !attrWithDot);
|
||||
}
|
||||
}
|
||||
if (failure != null)
|
||||
throw new Exception("TEST FAILED: " + failure);
|
||||
}
|
||||
|
||||
private static void test(
|
||||
MBeanServer mbs, ObjectName name, QueryExp[] queries, boolean expect)
|
||||
throws Exception {
|
||||
for (QueryExp query : queries) {
|
||||
// Serialize and deserialize the query to ensure that its
|
||||
// serialization is correct
|
||||
ByteArrayOutputStream bout = new ByteArrayOutputStream();
|
||||
ObjectOutputStream oout = new ObjectOutputStream(bout);
|
||||
oout.writeObject(query);
|
||||
oout.close();
|
||||
ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
|
||||
ObjectInputStream oin = new ObjectInputStream(bin);
|
||||
query = (QueryExp) oin.readObject();
|
||||
Set<ObjectName> names = mbs.queryNames(null, query);
|
||||
if (names.isEmpty()) {
|
||||
if (expect)
|
||||
fail("Query is false but should be true: " + query);
|
||||
} else if (names.equals(Collections.singleton(name))) {
|
||||
if (!expect)
|
||||
fail("Query is true but should be false: " + query);
|
||||
} else {
|
||||
fail("Query returned unexpected set: " + names);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void fail(String msg) {
|
||||
failure = msg;
|
||||
System.out.println("..." + msg);
|
||||
}
|
||||
}
|
@ -31,6 +31,10 @@
|
||||
* @run main QueryExpStringTest
|
||||
*/
|
||||
|
||||
// This test is mostly obsolete, since we now have Query.fromString.
|
||||
// The test includes its own parser, from which Query.fromString was derived.
|
||||
// The parsers are not identical and the one here is no longer maintained.
|
||||
|
||||
import java.util.*;
|
||||
import javax.management.*;
|
||||
|
||||
@ -39,6 +43,11 @@ public class QueryExpStringTest {
|
||||
private static final ValueExp
|
||||
attr = Query.attr("attr"),
|
||||
qattr = Query.attr("className", "attr"),
|
||||
aa = Query.attr("A"),
|
||||
bb = Query.attr("B"),
|
||||
cc = Query.attr("C"),
|
||||
dd = Query.attr("D"),
|
||||
zero = Query.value(0),
|
||||
classattr = Query.classattr(),
|
||||
simpleString = Query.value("simpleString"),
|
||||
complexString = Query.value("a'b\\'\""),
|
||||
@ -66,10 +75,14 @@ public class QueryExpStringTest {
|
||||
(StringValueExp) simpleString),
|
||||
initialStar = Query.initialSubString((AttributeValueExp) attr,
|
||||
Query.value("*")),
|
||||
initialPercent = Query.initialSubString((AttributeValueExp) attr,
|
||||
Query.value("%")),
|
||||
any = Query.anySubString((AttributeValueExp) attr,
|
||||
(StringValueExp) simpleString),
|
||||
anyStar = Query.anySubString((AttributeValueExp) attr,
|
||||
Query.value("*")),
|
||||
anyPercent = Query.anySubString((AttributeValueExp) attr,
|
||||
Query.value("%")),
|
||||
ffinal = Query.finalSubString((AttributeValueExp) attr,
|
||||
(StringValueExp) simpleString),
|
||||
finalMagic = Query.finalSubString((AttributeValueExp) attr,
|
||||
@ -77,16 +90,20 @@ public class QueryExpStringTest {
|
||||
in = Query.in(intValue, new ValueExp[] {intValue, floatValue}),
|
||||
and = Query.and(gt, lt),
|
||||
or = Query.or(gt, lt),
|
||||
not = Query.not(gt);
|
||||
not = Query.not(gt),
|
||||
aPlusB_PlusC = Query.gt(Query.plus(Query.plus(aa, bb), cc), zero),
|
||||
aPlus_BPlusC = Query.gt(Query.plus(aa, Query.plus(bb, cc)), zero);
|
||||
|
||||
// Commented-out tests below require change to implementation
|
||||
|
||||
private static final Object tests[] = {
|
||||
attr, "attr",
|
||||
qattr, "className.attr",
|
||||
// qattr, "className.attr",
|
||||
// Preceding form now appears as className#attr, an incompatible change
|
||||
// which we don't mind much because nobody uses the two-arg Query.attr.
|
||||
classattr, "Class",
|
||||
simpleString, "'simpleString'",
|
||||
// complexString, "'a\\'b\\\\\\'\"'",
|
||||
complexString, "'a''b\\\''\"'",
|
||||
intValue, "12345678",
|
||||
integerValue, "12345678",
|
||||
longValue, "12345678",
|
||||
@ -104,16 +121,20 @@ public class QueryExpStringTest {
|
||||
eq, "(12345678) = (2.5)",
|
||||
between, "(12345678) between (2.5) and (2.5)",
|
||||
match, "attr like 'simpleString'",
|
||||
// initial, "attr like 'simpleString*'",
|
||||
// initialStar, "attr like '\\\\**'",
|
||||
// any, "attr like '*simpleString*'",
|
||||
// anyStar, "attr like '*\\\\**'",
|
||||
// ffinal, "attr like '*simpleString'",
|
||||
// finalMagic, "attr like '*\\\\?\\\\*\\\\[\\\\\\\\'",
|
||||
initial, "attr like 'simpleString%'",
|
||||
initialStar, "attr like '\\*%'",
|
||||
initialPercent, "attr like '\\%%'",
|
||||
any, "attr like '%simpleString%'",
|
||||
anyStar, "attr like '%\\*%'",
|
||||
anyPercent, "attr like '%\\%%'",
|
||||
ffinal, "attr like '%simpleString'",
|
||||
finalMagic, "attr like '%\\?\\*\\[\\\\'",
|
||||
in, "12345678 in (12345678, 2.5)",
|
||||
and, "((12345678) > (2.5)) and ((12345678) < (2.5))",
|
||||
or, "((12345678) > (2.5)) or ((12345678) < (2.5))",
|
||||
not, "not ((12345678) > (2.5))",
|
||||
aPlusB_PlusC, "(A + B + C) > (0)",
|
||||
// aPlus_BPlusC, "(A + (B + C)) > (0)",
|
||||
};
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
@ -185,7 +206,9 @@ public class QueryExpStringTest {
|
||||
throw new Exception("Expected types `attr like string': " +
|
||||
exp + " like " + pat);
|
||||
}
|
||||
return Query.match((AttributeValueExp) exp, (StringValueExp) pat);
|
||||
StringValueExp spat = (StringValueExp) pat;
|
||||
spat = Query.value(translateMatch(spat.getValue()));
|
||||
return Query.match((AttributeValueExp) exp, spat);
|
||||
}
|
||||
|
||||
if (skip(ss, " in (")) {
|
||||
@ -203,6 +226,28 @@ public class QueryExpStringTest {
|
||||
throw new Exception("Expected in or like after expression");
|
||||
}
|
||||
|
||||
private static String translateMatch(String s) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < s.length(); i++) { // logic not correct for wide chars
|
||||
char c = s.charAt(i);
|
||||
switch (c) {
|
||||
case '\\':
|
||||
sb.append(c).append(s.charAt(++i)); break;
|
||||
case '%':
|
||||
sb.append('*'); break;
|
||||
case '_':
|
||||
sb.append('?'); break;
|
||||
case '*':
|
||||
sb.append("\\*"); break;
|
||||
case '?':
|
||||
sb.append("\\?"); break;
|
||||
default:
|
||||
sb.append(c); break;
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static QueryExp parseQueryAfterParen(String[] ss)
|
||||
throws Exception {
|
||||
/* This is very ugly. We might have "(q1) and (q2)" here, or
|
||||
@ -229,7 +274,7 @@ public class QueryExpStringTest {
|
||||
ss[0] = start;
|
||||
ValueExp lhs = parseExp(ss);
|
||||
if (!skip(ss, ") "))
|
||||
throw new Exception("Expected `) ' after subexpression");
|
||||
throw new Exception("Expected `) ' after subexpression: " + ss[0]);
|
||||
String op = scanWord(ss);
|
||||
if (!skip(ss, " ("))
|
||||
throw new Exception("Expected ` (' after `" + op + "'");
|
||||
@ -258,15 +303,16 @@ public class QueryExpStringTest {
|
||||
}
|
||||
|
||||
private static ValueExp parseExp(String[] ss) throws Exception {
|
||||
final ValueExp prim = parsePrimary(ss);
|
||||
ValueExp lhs = parsePrimary(ss);
|
||||
|
||||
while (true) {
|
||||
/* Look ahead to see if we have an arithmetic operator. */
|
||||
String back = ss[0];
|
||||
if (!skip(ss, " "))
|
||||
return prim;
|
||||
return lhs;
|
||||
if (ss[0].equals("") || "+-*/".indexOf(ss[0].charAt(0)) < 0) {
|
||||
ss[0] = back;
|
||||
return prim;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
final String op = scanWord(ss);
|
||||
@ -276,15 +322,16 @@ public class QueryExpStringTest {
|
||||
throw new Exception("Unknown arithmetic operator: " + op);
|
||||
if (!skip(ss, " "))
|
||||
throw new Exception("Expected space after arithmetic operator");
|
||||
ValueExp rhs = parseExp(ss);
|
||||
ValueExp rhs = parsePrimary(ss);
|
||||
switch (op.charAt(0)) {
|
||||
case '+': return Query.plus(prim, rhs);
|
||||
case '-': return Query.minus(prim, rhs);
|
||||
case '*': return Query.times(prim, rhs);
|
||||
case '/': return Query.div(prim, rhs);
|
||||
case '+': lhs = Query.plus(lhs, rhs); break;
|
||||
case '-': lhs = Query.minus(lhs, rhs); break;
|
||||
case '*': lhs = Query.times(lhs, rhs); break;
|
||||
case '/': lhs = Query.div(lhs, rhs); break;
|
||||
default: throw new Exception("Can't happen: " + op.charAt(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ValueExp parsePrimary(String[] ss) throws Exception {
|
||||
String s = ss[0];
|
||||
@ -324,14 +371,19 @@ public class QueryExpStringTest {
|
||||
private static String scanWord(String[] ss) throws Exception {
|
||||
String s = ss[0];
|
||||
int space = s.indexOf(' ');
|
||||
if (space < 0) {
|
||||
int rpar = s.indexOf(')');
|
||||
if (space < 0 && rpar < 0) {
|
||||
ss[0] = "";
|
||||
return s;
|
||||
} else {
|
||||
String word = s.substring(0, space);
|
||||
ss[0] = s.substring(space);
|
||||
return word;
|
||||
}
|
||||
int stop;
|
||||
if (space >= 0 && rpar >= 0) // string has both space and ), stop at first
|
||||
stop = Math.min(space, rpar);
|
||||
else // string has only one, stop at it
|
||||
stop = Math.max(space, rpar);
|
||||
String word = s.substring(0, stop);
|
||||
ss[0] = s.substring(stop);
|
||||
return word;
|
||||
}
|
||||
|
||||
private static boolean matchWord(String[] ss, String word)
|
||||
@ -381,13 +433,11 @@ public class QueryExpStringTest {
|
||||
for (i = 0; i < len; i++) {
|
||||
char c = s.charAt(i);
|
||||
if (c == '\'') {
|
||||
ss[0] = s.substring(i + 1);
|
||||
++i;
|
||||
if (i >= len || s.charAt(i) != '\'') {
|
||||
ss[0] = s.substring(i);
|
||||
return Query.value(buf.toString());
|
||||
}
|
||||
if (c == '\\') {
|
||||
if (++i == len)
|
||||
throw new Exception("\\ at end of string");
|
||||
c = s.charAt(i);
|
||||
}
|
||||
buf.append(c);
|
||||
}
|
||||
|
778
jdk/test/javax/management/query/QueryParseTest.java
Normal file
778
jdk/test/javax/management/query/QueryParseTest.java
Normal file
@ -0,0 +1,778 @@
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code 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 General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test QueryParseTest
|
||||
* @bug 6602310 6604768
|
||||
* @summary Test Query.fromString and Query.toString.
|
||||
* @author Eamonn McManus
|
||||
*/
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import javax.management.Attribute;
|
||||
import javax.management.AttributeList;
|
||||
import javax.management.AttributeNotFoundException;
|
||||
import javax.management.DynamicMBean;
|
||||
import javax.management.MBeanAttributeInfo;
|
||||
import javax.management.MBeanInfo;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerDelegate;
|
||||
import javax.management.MBeanServerFactory;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.Query;
|
||||
import javax.management.QueryExp;
|
||||
|
||||
public class QueryParseTest {
|
||||
// In this table, each string constant corresponds to a test case.
|
||||
// The objects following the string up to the next string are MBeans.
|
||||
// Each MBean must implement ExpectedValue to return true or false
|
||||
// according as it should return that value for the query parsed
|
||||
// from the given string. The test will parse the string into a
|
||||
// a query and verify that the MBeans return the expected value
|
||||
// for that query. Then it will convert the query back into a string
|
||||
// and into a second query, and check that the MBeans return the
|
||||
// expected value for that query too. The reason we need to do all
|
||||
// this is that the spec talks about "equivalent queries", and gives
|
||||
// the implementation wide scope to rearrange queries. So we cannot
|
||||
// just compare string values.
|
||||
//
|
||||
// We could also write an implementation-dependent test that knew what
|
||||
// the strings look like, and that would have to be changed if the
|
||||
// implementation changed. But the approach here is cleaner.
|
||||
//
|
||||
// To simplify the creation of MBeans, most use the expectTrue or
|
||||
// expectFalse methods. The parameters of these methods end up in
|
||||
// attributes called "A", "B", "C", etc.
|
||||
private static final Object[] queryTests = {
|
||||
// RELATIONS
|
||||
|
||||
"A < B",
|
||||
expectTrue(1, 2), expectTrue(1.0, 2.0), expectTrue("one", "two"),
|
||||
expectTrue(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY),
|
||||
expectFalse(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY),
|
||||
expectFalse(1, 1), expectFalse(1.0, 1.0), expectFalse("one", "one"),
|
||||
expectFalse(2, 1), expectFalse(2.0, 1.0), expectFalse("two", "one"),
|
||||
expectFalse(Double.NaN, Double.NaN),
|
||||
|
||||
"One = two",
|
||||
expectTrueOneTwo(1, 1), expectTrueOneTwo(1.0, 1.0),
|
||||
expectFalseOneTwo(1, 2), expectFalseOneTwo(2, 1),
|
||||
|
||||
"A <= B",
|
||||
expectTrue(1, 1), expectTrue(1, 2), expectTrue("one", "one"),
|
||||
expectTrue("one", "two"),
|
||||
expectFalse(2, 1), expectFalse("two", "one"),
|
||||
expectFalse(Double.NaN, Double.NaN),
|
||||
|
||||
"A >= B",
|
||||
expectTrue(1, 1), expectTrue(2, 1), expectTrue("two", "one"),
|
||||
expectFalse(1, 2), expectFalse("one", "two"),
|
||||
|
||||
"A > B",
|
||||
expectTrue(2, 1), expectTrue("two", "one"),
|
||||
expectFalse(2, 2), expectFalse(1, 2), expectFalse(1.0, 2.0),
|
||||
expectFalse("one", "two"),
|
||||
|
||||
"A <> B",
|
||||
expectTrue(1, 2), expectTrue("foo", "bar"),
|
||||
expectFalse(1, 1), expectFalse("foo", "foo"),
|
||||
|
||||
"A != B",
|
||||
expectTrue(1, 2), expectTrue("foo", "bar"),
|
||||
expectFalse(1, 1), expectFalse("foo", "foo"),
|
||||
|
||||
// PARENTHESES
|
||||
|
||||
"(((A))) = (B)",
|
||||
expectTrue(1, 1), expectFalse(1, 2),
|
||||
|
||||
"(A = B)",
|
||||
expectTrue(1, 1), expectFalse(1, 2),
|
||||
|
||||
"(((A = (B))))",
|
||||
expectTrue(1, 1), expectFalse(1, 2),
|
||||
|
||||
// INTEGER LITERALS
|
||||
|
||||
"A = 1234567890123456789",
|
||||
expectTrue(1234567890123456789L), expectFalse(123456789L),
|
||||
|
||||
"A = +1234567890123456789",
|
||||
expectTrue(1234567890123456789L), expectFalse(123456789L),
|
||||
|
||||
"A = -1234567890123456789",
|
||||
expectTrue(-1234567890123456789L), expectFalse(-123456789L),
|
||||
|
||||
|
||||
"A = + 1234567890123456789",
|
||||
expectTrue(1234567890123456789L), expectFalse(123456789L),
|
||||
|
||||
"A = - 1234567890123456789",
|
||||
expectTrue(-1234567890123456789L), expectFalse(-123456789L),
|
||||
|
||||
"A = " + Long.MAX_VALUE,
|
||||
expectTrue(Long.MAX_VALUE), expectFalse(Long.MIN_VALUE),
|
||||
|
||||
"A = " + Long.MIN_VALUE,
|
||||
expectTrue(Long.MIN_VALUE), expectFalse(Long.MAX_VALUE),
|
||||
|
||||
// DOUBLE LITERALS
|
||||
|
||||
"A = 0.0",
|
||||
expectTrue(0.0), expectFalse(1.0),
|
||||
|
||||
"A = 0.0e23",
|
||||
expectTrue(0.0), expectFalse(1.0),
|
||||
|
||||
"A = 1.2e3",
|
||||
expectTrue(1.2e3), expectFalse(1.2),
|
||||
|
||||
"A = +1.2",
|
||||
expectTrue(1.2), expectFalse(-1.2),
|
||||
|
||||
"A = 1.2e+3",
|
||||
expectTrue(1.2e3), expectFalse(1.2),
|
||||
|
||||
"A = 1.2e-3",
|
||||
expectTrue(1.2e-3), expectFalse(1.2),
|
||||
|
||||
"A = 1.2E3",
|
||||
expectTrue(1.2e3), expectFalse(1.2),
|
||||
|
||||
"A = -1.2e3",
|
||||
expectTrue(-1.2e3), expectFalse(1.2),
|
||||
|
||||
"A = " + Double.MAX_VALUE,
|
||||
expectTrue(Double.MAX_VALUE), expectFalse(Double.MIN_VALUE),
|
||||
|
||||
"A = " + -Double.MAX_VALUE,
|
||||
expectTrue(-Double.MAX_VALUE), expectFalse(-Double.MIN_VALUE),
|
||||
|
||||
"A = " + Double.MIN_VALUE,
|
||||
expectTrue(Double.MIN_VALUE), expectFalse(Double.MAX_VALUE),
|
||||
|
||||
"A = " + -Double.MIN_VALUE,
|
||||
expectTrue(-Double.MIN_VALUE), expectFalse(-Double.MAX_VALUE),
|
||||
|
||||
Query.toString( // A = Infinity -> A = (1.0/0.0)
|
||||
Query.eq(Query.attr("A"), Query.value(Double.POSITIVE_INFINITY))),
|
||||
expectTrue(Double.POSITIVE_INFINITY),
|
||||
expectFalse(0.0), expectFalse(Double.NEGATIVE_INFINITY),
|
||||
|
||||
Query.toString( // A = -Infinity -> A = (-1.0/0.0)
|
||||
Query.eq(Query.attr("A"), Query.value(Double.NEGATIVE_INFINITY))),
|
||||
expectTrue(Double.NEGATIVE_INFINITY),
|
||||
expectFalse(0.0), expectFalse(Double.POSITIVE_INFINITY),
|
||||
|
||||
Query.toString( // A < NaN -> A < (0.0/0.0)
|
||||
Query.lt(Query.attr("A"), Query.value(Double.NaN))),
|
||||
expectFalse(0.0), expectFalse(Double.NEGATIVE_INFINITY),
|
||||
expectFalse(Double.POSITIVE_INFINITY), expectFalse(Double.NaN),
|
||||
|
||||
Query.toString( // A >= NaN -> A < (0.0/0.0)
|
||||
Query.geq(Query.attr("A"), Query.value(Double.NaN))),
|
||||
expectFalse(0.0), expectFalse(Double.NEGATIVE_INFINITY),
|
||||
expectFalse(Double.POSITIVE_INFINITY), expectFalse(Double.NaN),
|
||||
|
||||
// STRING LITERALS
|
||||
|
||||
"A = 'blim'",
|
||||
expectTrue("blim"), expectFalse("blam"),
|
||||
|
||||
"A = 'can''t'",
|
||||
expectTrue("can't"), expectFalse("cant"), expectFalse("can''t"),
|
||||
|
||||
"A = '''blim'''",
|
||||
expectTrue("'blim'"), expectFalse("'blam'"),
|
||||
|
||||
"A = ''",
|
||||
expectTrue(""), expectFalse((Object) null),
|
||||
|
||||
// BOOLEAN LITERALS
|
||||
|
||||
"A = true",
|
||||
expectTrue(true), expectFalse(false), expectFalse((Object) null),
|
||||
|
||||
"A = TRUE",
|
||||
expectTrue(true), expectFalse(false),
|
||||
|
||||
"A = TrUe",
|
||||
expectTrue(true), expectFalse(false),
|
||||
|
||||
"A = false",
|
||||
expectTrue(false), expectFalse(true),
|
||||
|
||||
"A = fAlSe",
|
||||
expectTrue(false), expectFalse(true),
|
||||
|
||||
"A = \"true\"", // An attribute called "true"
|
||||
expectFalse(true), expectFalse(false), expectFalse("\"true\""),
|
||||
newTester(new String[] {"A", "true"}, new Object[] {2.2, 2.2}, true),
|
||||
newTester(new String[] {"A", "true"}, new Object[] {2.2, 2.3}, false),
|
||||
|
||||
"A = \"False\"",
|
||||
expectFalse(true), expectFalse(false), expectFalse("\"False\""),
|
||||
newTester(new String[] {"A", "False"}, new Object[] {2.2, 2.2}, true),
|
||||
newTester(new String[] {"A", "False"}, new Object[] {2.2, 2.3}, false),
|
||||
|
||||
// ARITHMETIC
|
||||
|
||||
"A + B = 10",
|
||||
expectTrue(4, 6), expectFalse(3, 8),
|
||||
|
||||
"A + B = 'blim'",
|
||||
expectTrue("bl", "im"), expectFalse("bl", "am"),
|
||||
|
||||
"A - B = 10",
|
||||
expectTrue(16, 6), expectFalse(16, 3),
|
||||
|
||||
"A * B = 10",
|
||||
expectTrue(2, 5), expectFalse(3, 3),
|
||||
|
||||
"A / B = 10",
|
||||
expectTrue(70, 7), expectTrue(70.0, 7), expectFalse(70.01, 7),
|
||||
|
||||
"A + B + C = 10",
|
||||
expectTrue(2, 3, 5), expectFalse(2, 4, 8),
|
||||
|
||||
"A+B+C=10",
|
||||
expectTrue(2, 3, 5), expectFalse(2, 4, 8),
|
||||
|
||||
"A + B + C + D = 10",
|
||||
expectTrue(1, 2, 3, 4), expectFalse(2, 3, 4, 5),
|
||||
|
||||
"A + (B + C) = 10",
|
||||
expectTrue(2, 3, 5), expectFalse(2, 4, 8),
|
||||
|
||||
// It is not correct to rearrange A + (B + C) as A + B + C
|
||||
// (which means (A + B) + C), because of overflow.
|
||||
// In particular Query.toString must not do this.
|
||||
"A + (B + C) = " + Double.MAX_VALUE, // ensure no false associativity
|
||||
expectTrue(Double.MAX_VALUE, Double.MAX_VALUE, -Double.MAX_VALUE),
|
||||
expectFalse(-Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE),
|
||||
|
||||
"A * (B * C) < " + Double.MAX_VALUE, // same test for multiplication
|
||||
expectTrue(Double.MAX_VALUE, Double.MAX_VALUE, Double.MIN_VALUE),
|
||||
expectFalse(Double.MIN_VALUE, Double.MAX_VALUE, Double.MAX_VALUE),
|
||||
|
||||
"A * B + C = 10",
|
||||
expectTrue(3, 3, 1), expectTrue(2, 4, 2), expectFalse(1, 2, 3),
|
||||
|
||||
"A*B+C=10",
|
||||
expectTrue(3, 3, 1), expectTrue(2, 4, 2), expectFalse(1, 2, 3),
|
||||
|
||||
"(A * B) + C = 10",
|
||||
expectTrue(3, 3, 1), expectTrue(2, 4, 2), expectFalse(1, 2, 3),
|
||||
|
||||
"A + B * C = 10",
|
||||
expectTrue(1, 3, 3), expectTrue(2, 2, 4), expectFalse(1, 2, 3),
|
||||
|
||||
"A - B * C = 10",
|
||||
expectTrue(16, 2, 3), expectFalse(15, 2, 2),
|
||||
|
||||
"A + B / C = 10",
|
||||
expectTrue(5, 15, 3), expectFalse(5, 16, 4),
|
||||
|
||||
"A - B / C = 10",
|
||||
expectTrue(16, 12, 2), expectFalse(15, 10, 3),
|
||||
|
||||
"A * (B + C) = 10",
|
||||
expectTrue(2, 2, 3), expectFalse(1, 2, 3),
|
||||
|
||||
"A / (B + C) = 10",
|
||||
expectTrue(70, 4, 3), expectFalse(70, 3, 5),
|
||||
|
||||
"A * (B - C) = 10",
|
||||
expectTrue(2, 8, 3), expectFalse(2, 3, 8),
|
||||
|
||||
"A / (B - C) = 10",
|
||||
expectTrue(70, 11, 4), expectFalse(70, 4, 11),
|
||||
|
||||
"A / B / C = 10",
|
||||
expectTrue(140, 2, 7), expectFalse(100, 5, 5),
|
||||
|
||||
"A / (B / C) = 10",
|
||||
expectTrue(70, 14, 2), expectFalse(70, 10, 7),
|
||||
|
||||
// LOGIC
|
||||
|
||||
"A = B or C = D",
|
||||
expectTrue(1, 1, 2, 3), expectTrue(1, 2, 3, 3), expectTrue(1, 1, 2, 2),
|
||||
expectFalse(1, 2, 3, 4), expectFalse("!", "!!", "?", "??"),
|
||||
|
||||
"A = B and C = D",
|
||||
expectTrue(1, 1, 2, 2),
|
||||
expectFalse(1, 1, 2, 3), expectFalse(1, 2, 3, 3),
|
||||
|
||||
"A = 1 and B = 2 and C = 3",
|
||||
expectTrue(1, 2, 3), expectFalse(1, 2, 4),
|
||||
|
||||
"A = 1 or B = 2 or C = 3",
|
||||
expectTrue(1, 2, 3), expectTrue(1, 0, 0), expectTrue(0, 0, 3),
|
||||
expectFalse(2, 3, 4),
|
||||
|
||||
// grouped as (a and b) or (c and d)
|
||||
"A = 1 AND B = 2 OR C = 3 AND D = 4",
|
||||
expectTrue(1, 2, 3, 4), expectTrue(1, 2, 1, 2), expectTrue(3, 4, 3, 4),
|
||||
expectFalse(3, 4, 1, 2), expectFalse(1, 1, 1, 1),
|
||||
|
||||
"(A = 1 AND B = 2) OR (C = 3 AND D = 4)",
|
||||
expectTrue(1, 2, 3, 4), expectTrue(1, 2, 1, 2), expectTrue(3, 4, 3, 4),
|
||||
expectFalse(3, 4, 1, 2), expectFalse(1, 1, 1, 1),
|
||||
|
||||
"(A = 1 or B = 2) AND (C = 3 or C = 4)",
|
||||
expectTrue(1, 1, 3, 3), expectTrue(2, 2, 4, 4), expectTrue(1, 2, 3, 4),
|
||||
expectFalse(1, 2, 1, 2), expectFalse(3, 4, 3, 4),
|
||||
|
||||
// LIKE
|
||||
|
||||
"A like 'b%m'",
|
||||
expectTrue("blim"), expectTrue("bm"),
|
||||
expectFalse(""), expectFalse("blimmo"), expectFalse("mmm"),
|
||||
|
||||
"A not like 'b%m'",
|
||||
expectFalse("blim"), expectFalse("bm"),
|
||||
expectTrue(""), expectTrue("blimmo"), expectTrue("mmm"),
|
||||
|
||||
"A like 'b_m'",
|
||||
expectTrue("bim"), expectFalse("blim"),
|
||||
|
||||
"A like '%can''t%'",
|
||||
expectTrue("can't"),
|
||||
expectTrue("I'm sorry Dave, I'm afraid I can't do that"),
|
||||
expectFalse("cant"), expectFalse("can''t"),
|
||||
|
||||
"A like '\\%%\\%'",
|
||||
expectTrue("%blim%"), expectTrue("%%"),
|
||||
expectFalse("blim"), expectFalse("%asdf"), expectFalse("asdf%"),
|
||||
|
||||
"A LIKE '*%?_'",
|
||||
expectTrue("*blim?!"), expectTrue("*?_"),
|
||||
expectFalse("blim"), expectFalse("blim?"),
|
||||
expectFalse("?*"), expectFalse("??"), expectFalse(""), expectFalse("?"),
|
||||
|
||||
Query.toString(
|
||||
Query.initialSubString(Query.attr("A"), Query.value("*?%_"))),
|
||||
expectTrue("*?%_tiddly"), expectTrue("*?%_"),
|
||||
expectFalse("?%_tiddly"), expectFalse("*!%_"), expectFalse("*??_"),
|
||||
expectFalse("*?%!"), expectFalse("*?%!tiddly"),
|
||||
|
||||
Query.toString(
|
||||
Query.finalSubString(Query.attr("A"), Query.value("*?%_"))),
|
||||
expectTrue("tiddly*?%_"), expectTrue("*?%_"),
|
||||
expectFalse("tiddly?%_"), expectFalse("*!%_"), expectFalse("*??_"),
|
||||
expectFalse("*?%!"), expectFalse("tiddly*?%!"),
|
||||
|
||||
// BETWEEN
|
||||
|
||||
"A between B and C",
|
||||
expectTrue(1, 1, 2), expectTrue(2, 1, 2), expectTrue(2, 1, 3),
|
||||
expectFalse(3, 1, 2), expectFalse(0, 1, 2), expectFalse(2, 3, 1),
|
||||
expectTrue(1.0, 0.0, 2.0), expectFalse(2.0, 0.0, 1.0),
|
||||
expectTrue(0.0, 0.0, 0.0), expectTrue(1.0, 0.0, 1.0),
|
||||
expectTrue(1.0, 0.0, Double.POSITIVE_INFINITY),
|
||||
expectFalse(1.0, Double.NEGATIVE_INFINITY, 0.0),
|
||||
expectFalse(false, false, true), expectFalse(true, false, true),
|
||||
expectTrue("jim", "fred", "sheila"), expectFalse("fred", "jim", "sheila"),
|
||||
|
||||
"A between B and C and 1+2=3",
|
||||
expectTrue(2, 1, 3), expectFalse(2, 3, 1),
|
||||
|
||||
"A not between B and C",
|
||||
expectTrue(1, 2, 3), expectFalse(2, 1, 3),
|
||||
|
||||
// IN
|
||||
|
||||
"A in (1, 2, 3)",
|
||||
expectTrue(1), expectTrue(2), expectTrue(3),
|
||||
expectFalse(0), expectFalse(4),
|
||||
|
||||
"A in (1)",
|
||||
expectTrue(1), expectFalse(0),
|
||||
|
||||
"A in (1.2, 3.4)",
|
||||
expectTrue(1.2), expectTrue(3.4), expectFalse(0.0),
|
||||
|
||||
"A in ('foo', 'bar')",
|
||||
expectTrue("foo"), expectTrue("bar"), expectFalse("baz"),
|
||||
|
||||
"A in ('foo', 'bar') and 'bl'+'im'='blim'",
|
||||
expectTrue("foo"), expectTrue("bar"), expectFalse("baz"),
|
||||
|
||||
"A in (B, C, D)", // requires fix for CR 6604768
|
||||
expectTrue(1, 1, 2, 3), expectFalse(1, 2, 3, 4),
|
||||
|
||||
"A not in (B, C, D)",
|
||||
expectTrue(1, 2, 3, 4), expectFalse(1, 1, 2, 3),
|
||||
|
||||
// QUOTING
|
||||
|
||||
"\"LIKE\" = 1 and \"NOT\" = 2 and \"INSTANCEOF\" = 3 and " +
|
||||
"\"TRUE\" = 4 and \"FALSE\" = 5",
|
||||
newTester(
|
||||
new String[] {"LIKE", "NOT", "INSTANCEOF", "TRUE", "FALSE"},
|
||||
new Object[] {1, 2, 3, 4, 5},
|
||||
true),
|
||||
newTester(
|
||||
new String[] {"LIKE", "NOT", "INSTANCEOF", "TRUE", "FALSE"},
|
||||
new Object[] {5, 4, 3, 2, 1},
|
||||
false),
|
||||
|
||||
"\"\"\"woo\"\"\" = 5",
|
||||
newTester(new String[] {"\"woo\""}, new Object[] {5}, true),
|
||||
newTester(new String[] {"\"woo\""}, new Object[] {4}, false),
|
||||
expectFalse(),
|
||||
|
||||
// INSTANCEOF
|
||||
|
||||
"instanceof '" + Tester.class.getName() + "'",
|
||||
expectTrue(),
|
||||
|
||||
"instanceof '" + String.class.getName() + "'",
|
||||
expectFalse(),
|
||||
|
||||
// LIKE OBJECTNAME
|
||||
|
||||
// The test MBean is registered as a:b=c
|
||||
"like 'a:b=c'", expectTrue(),
|
||||
"like 'a:*'", expectTrue(),
|
||||
"like '*:b=c'", expectTrue(),
|
||||
"like 'a:b=*'", expectTrue(),
|
||||
"like 'a:b=?'", expectTrue(),
|
||||
"like 'd:b=c'", expectFalse(),
|
||||
"like 'a:b=??*'", expectFalse(),
|
||||
"like 'a:b=\"can''t\"'", expectFalse(),
|
||||
|
||||
// QUALIFIED ATTRIBUTE
|
||||
|
||||
Tester.class.getName() + "#A = 5",
|
||||
expectTrue(5), expectFalse(4),
|
||||
|
||||
Tester.class.getName() + " # A = 5",
|
||||
expectTrue(5), expectFalse(4),
|
||||
|
||||
Tester.class.getSuperclass().getName() + "#A = 5",
|
||||
expectFalse(5),
|
||||
|
||||
DynamicMBean.class.getName() + "#A = 5",
|
||||
expectFalse(5),
|
||||
|
||||
Tester.class.getName() + "#A = 5",
|
||||
new Tester(new String[] {"A"}, new Object[] {5}, false) {},
|
||||
// note the little {} at the end which means this is a subclass
|
||||
// and therefore QualifiedAttributeValue should return false.
|
||||
|
||||
MBeanServerDelegate.class.getName() + "#SpecificationName LIKE '%'",
|
||||
new Wrapped(new MBeanServerDelegate(), true),
|
||||
new Tester(new String[] {"SpecificationName"}, new Object[] {"JMX"}, false),
|
||||
|
||||
// DOTTED ATTRIBUTE
|
||||
|
||||
"A.canonicalName = '" +
|
||||
MBeanServerDelegate.DELEGATE_NAME.getCanonicalName() + "'",
|
||||
expectTrue(MBeanServerDelegate.DELEGATE_NAME),
|
||||
expectFalse(ObjectName.WILDCARD),
|
||||
|
||||
"A.class.name = 'java.lang.String'",
|
||||
expectTrue("blim"), expectFalse(95), expectFalse((Object) null),
|
||||
|
||||
"A.canonicalName like 'JMImpl%:%'",
|
||||
expectTrue(MBeanServerDelegate.DELEGATE_NAME),
|
||||
expectFalse(ObjectName.WILDCARD),
|
||||
|
||||
"A.true = 'blim'",
|
||||
new Tester(new String[] {"A.true"}, new Object[] {"blim"}, true),
|
||||
new Tester(new String[] {"A.true"}, new Object[] {"blam"}, false),
|
||||
|
||||
"\"A.true\" = 'blim'",
|
||||
new Tester(new String[] {"A.true"}, new Object[] {"blim"}, true),
|
||||
new Tester(new String[] {"A.true"}, new Object[] {"blam"}, false),
|
||||
|
||||
MBeanServerDelegate.class.getName() +
|
||||
"#SpecificationName.class.name = 'java.lang.String'",
|
||||
new Wrapped(new MBeanServerDelegate(), true),
|
||||
new Tester(new String[] {"SpecificationName"}, new Object[] {"JMX"}, false),
|
||||
|
||||
MBeanServerDelegate.class.getName() +
|
||||
" # SpecificationName.class.name = 'java.lang.String'",
|
||||
new Wrapped(new MBeanServerDelegate(), true),
|
||||
new Tester(new String[] {"SpecificationName"}, new Object[] {"JMX"}, false),
|
||||
|
||||
// CLASS
|
||||
|
||||
"class = '" + Tester.class.getName() + "'",
|
||||
expectTrue(),
|
||||
new Wrapped(new MBeanServerDelegate(), false),
|
||||
|
||||
"Class = '" + Tester.class.getName() + "'",
|
||||
expectTrue(),
|
||||
new Wrapped(new MBeanServerDelegate(), false),
|
||||
};
|
||||
|
||||
private static final String[] incorrectQueries = {
|
||||
"", " ", "25", "()", "(a = b", "a = b)", "a.3 = 5",
|
||||
"a = " + Long.MAX_VALUE + "0",
|
||||
"a = " + Double.MAX_VALUE + "0",
|
||||
"a = " + Double.MIN_VALUE + "0",
|
||||
"a = 12a5", "a = 12e5e5", "a = 12.23.34",
|
||||
"a = 'can't'", "a = 'unterminated", "a = 'asdf''",
|
||||
"a = \"oops", "a = \"oops\"\"",
|
||||
"a like 5", "true or false",
|
||||
"a ! b", "? = 3", "a = @", "a##b",
|
||||
"a between b , c", "a between and c",
|
||||
"a in b, c", "a in 23", "a in (2, 3", "a in (2, 3x)",
|
||||
"a like \"foo\"", "a like b", "a like 23",
|
||||
"like \"foo\"", "like b", "like 23", "like 'a:b'",
|
||||
"5 like 'a'", "'a' like '%'",
|
||||
"a not= b", "a not = b", "a not b", "a not b c",
|
||||
"a = +b", "a = +'b'", "a = +true", "a = -b", "a = -'b'",
|
||||
"a#5 = b", "a#'b' = c",
|
||||
"a instanceof b", "a instanceof 17", "a instanceof",
|
||||
"a like 'oops\\'", "a like '[oops'",
|
||||
|
||||
// Check that -Long.MIN_VALUE is an illegal constant. This is one more
|
||||
// than Long.MAX_VALUE and, like the Java language, we only allow it
|
||||
// if it is the operand of unary minus.
|
||||
"a = " + Long.toString(Long.MIN_VALUE).substring(1),
|
||||
};
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
int nexti;
|
||||
String failed = null;
|
||||
|
||||
System.out.println("TESTING CORRECT QUERY STRINGS");
|
||||
for (int i = 0; i < queryTests.length; i = nexti) {
|
||||
for (nexti = i + 1; nexti < queryTests.length; nexti++) {
|
||||
if (queryTests[nexti] instanceof String)
|
||||
break;
|
||||
}
|
||||
if (!(queryTests[i] instanceof String))
|
||||
throw new Exception("Test bug: should be string: " + queryTests[i]);
|
||||
|
||||
String qs = (String) queryTests[i];
|
||||
System.out.println("Test: " + qs);
|
||||
|
||||
QueryExp qe = Query.fromString(qs);
|
||||
String qes = Query.toString(qe);
|
||||
System.out.println("...parses to: " + qes);
|
||||
final QueryExp[] queries;
|
||||
if (qes.equals(qs))
|
||||
queries = new QueryExp[] {qe};
|
||||
else {
|
||||
QueryExp qe2 = Query.fromString(qes);
|
||||
String qes2 = Query.toString(qe2);
|
||||
System.out.println("...which parses to: " + qes2);
|
||||
if (qes.equals(qes2))
|
||||
queries = new QueryExp[] {qe};
|
||||
else
|
||||
queries = new QueryExp[] {qe, qe2};
|
||||
}
|
||||
|
||||
for (int j = i + 1; j < nexti; j++) {
|
||||
Object mbean;
|
||||
if (queryTests[j] instanceof Wrapped)
|
||||
mbean = ((Wrapped) queryTests[j]).mbean();
|
||||
else
|
||||
mbean = queryTests[j];
|
||||
boolean expect = ((ExpectedValue) queryTests[j]).expectedValue();
|
||||
for (QueryExp qet : queries) {
|
||||
boolean actual = runQuery(qet, mbean);
|
||||
boolean ok = (expect == actual);
|
||||
System.out.println(
|
||||
"..." + mbean + " -> " + actual +
|
||||
(ok ? " (OK)" : " ####INCORRECT####"));
|
||||
if (!ok)
|
||||
failed = qs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println();
|
||||
System.out.println("TESTING INCORRECT QUERY STRINGS");
|
||||
for (String s : incorrectQueries) {
|
||||
try {
|
||||
QueryExp qe = Query.fromString(s);
|
||||
System.out.println("###DID NOT GET ERROR:### \"" + s + "\"");
|
||||
failed = s;
|
||||
} catch (IllegalArgumentException e) {
|
||||
String es = (e.getClass() == IllegalArgumentException.class) ?
|
||||
e.getMessage() : e.toString();
|
||||
System.out.println("OK: exception for \"" + s + "\": " + es);
|
||||
}
|
||||
}
|
||||
|
||||
if (failed == null)
|
||||
System.out.println("TEST PASSED");
|
||||
else
|
||||
throw new Exception("TEST FAILED: Last failure: " + failed);
|
||||
}
|
||||
|
||||
private static boolean runQuery(QueryExp qe, Object mbean)
|
||||
throws Exception {
|
||||
MBeanServer mbs = MBeanServerFactory.newMBeanServer();
|
||||
ObjectName name = new ObjectName("a:b=c");
|
||||
mbs.registerMBean(mbean, name);
|
||||
Set<ObjectName> names = mbs.queryNames(new ObjectName("a:*"), qe);
|
||||
if (names.isEmpty())
|
||||
return false;
|
||||
if (names.equals(Collections.singleton(name)))
|
||||
return true;
|
||||
throw new Exception("Unexpected query result set: " + names);
|
||||
}
|
||||
|
||||
private static interface ExpectedValue {
|
||||
public boolean expectedValue();
|
||||
}
|
||||
|
||||
private static class Wrapped implements ExpectedValue {
|
||||
private final Object mbean;
|
||||
private final boolean expect;
|
||||
|
||||
Wrapped(Object mbean, boolean expect) {
|
||||
this.mbean = mbean;
|
||||
this.expect = expect;
|
||||
}
|
||||
|
||||
Object mbean() {
|
||||
return mbean;
|
||||
}
|
||||
|
||||
public boolean expectedValue() {
|
||||
return expect;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Tester implements DynamicMBean, ExpectedValue {
|
||||
private final AttributeList attributes;
|
||||
private final boolean expectedValue;
|
||||
|
||||
Tester(AttributeList attributes, boolean expectedValue) {
|
||||
this.attributes = attributes;
|
||||
this.expectedValue = expectedValue;
|
||||
}
|
||||
|
||||
Tester(String[] names, Object[] values, boolean expectedValue) {
|
||||
this(makeAttributeList(names, values), expectedValue);
|
||||
}
|
||||
|
||||
private static AttributeList makeAttributeList(
|
||||
String[] names, Object[] values) {
|
||||
if (names.length != values.length)
|
||||
throw new Error("Test bug: names and values different length");
|
||||
AttributeList list = new AttributeList();
|
||||
for (int i = 0; i < names.length; i++)
|
||||
list.add(new Attribute(names[i], values[i]));
|
||||
return list;
|
||||
}
|
||||
|
||||
public Object getAttribute(String attribute)
|
||||
throws AttributeNotFoundException {
|
||||
for (Attribute a : attributes.asList()) {
|
||||
if (a.getName().equals(attribute))
|
||||
return a.getValue();
|
||||
}
|
||||
throw new AttributeNotFoundException(attribute);
|
||||
}
|
||||
|
||||
public void setAttribute(Attribute attribute) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public AttributeList getAttributes(String[] attributes) {
|
||||
AttributeList list = new AttributeList();
|
||||
for (String attribute : attributes) {
|
||||
try {
|
||||
list.add(new Attribute(attribute, getAttribute(attribute)));
|
||||
} catch (AttributeNotFoundException e) {
|
||||
// OK: ignore, per semantics of getAttributes
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public AttributeList setAttributes(AttributeList attributes) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public Object invoke(String actionName, Object[] params, String[] signature) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public MBeanInfo getMBeanInfo() {
|
||||
MBeanAttributeInfo mbais[] = new MBeanAttributeInfo[attributes.size()];
|
||||
for (int i = 0; i < mbais.length; i++) {
|
||||
Attribute attr = attributes.asList().get(i);
|
||||
String name = attr.getName();
|
||||
Object value = attr.getValue();
|
||||
String type =
|
||||
((value == null) ? new Object() : value).getClass().getName();
|
||||
mbais[i] = new MBeanAttributeInfo(
|
||||
name, type, name, true, false, false);
|
||||
}
|
||||
return new MBeanInfo(
|
||||
getClass().getName(), "descr", mbais, null, null, null);
|
||||
}
|
||||
|
||||
public boolean expectedValue() {
|
||||
return expectedValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return attributes.toString();
|
||||
}
|
||||
}
|
||||
|
||||
// Method rather than field, to avoid circular init dependencies
|
||||
private static String[] abcd() {
|
||||
return new String[] {"A", "B", "C", "D"};
|
||||
}
|
||||
|
||||
private static String[] onetwo() {
|
||||
return new String[] {"One", "two"};
|
||||
}
|
||||
|
||||
private static Object expectTrue(Object... attrs) {
|
||||
return newTester(abcd(), attrs, true);
|
||||
}
|
||||
|
||||
private static Object expectFalse(Object... attrs) {
|
||||
return newTester(abcd(), attrs, false);
|
||||
}
|
||||
|
||||
private static Object expectTrueOneTwo(Object... attrs) {
|
||||
return newTester(onetwo(), attrs, true);
|
||||
}
|
||||
|
||||
private static Object expectFalseOneTwo(Object... attrs) {
|
||||
return newTester(onetwo(), attrs, false);
|
||||
}
|
||||
|
||||
private static Object newTester(String[] names, Object[] attrs, boolean expect) {
|
||||
AttributeList list = new AttributeList();
|
||||
for (int i = 0; i < attrs.length; i++)
|
||||
list.add(new Attribute(names[i], attrs[i]));
|
||||
return new Tester(list, expect);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user