38 * true when querying a remote MBean Server: a custom implementation of the |
38 * true when querying a remote MBean Server: a custom implementation of the |
39 * {@code QueryExp} interface might not be present in the remote MBean Server, |
39 * {@code QueryExp} interface might not be present in the remote MBean Server, |
40 * but the methods in this class return only standard classes that are |
40 * but the methods in this class return only standard classes that are |
41 * part of the JMX implementation.</p> |
41 * part of the JMX implementation.</p> |
42 * |
42 * |
43 * <p>There are two ways to create {@code QueryExp} objects using the methods |
|
44 * in this class. The first is to build them by chaining together calls to |
|
45 * the various methods. The second is to use the Query Language described |
|
46 * <a href="#ql">below</a> and produce the {@code QueryExp} by calling |
|
47 * {@link #fromString Query.fromString}. The two ways are equivalent: |
|
48 * every {@code QueryExp} returned by {@code fromString} can also be |
|
49 * constructed by chaining method calls.</p> |
|
50 * |
|
51 * <p>As an example, suppose you wanted to find all MBeans where the {@code |
43 * <p>As an example, suppose you wanted to find all MBeans where the {@code |
52 * Enabled} attribute is {@code true} and the {@code Owner} attribute is {@code |
44 * Enabled} attribute is {@code true} and the {@code Owner} attribute is {@code |
53 * "Duke"}. Here is how you could construct the appropriate {@code QueryExp} by |
45 * "Duke"}. Here is how you could construct the appropriate {@code QueryExp} by |
54 * chaining together method calls:</p> |
46 * chaining together method calls:</p> |
55 * |
47 * |
56 * <pre> |
48 * <pre> |
57 * QueryExp query = |
49 * QueryExp query = |
58 * Query.and(Query.eq(Query.attr("Enabled"), Query.value(true)), |
50 * Query.and(Query.eq(Query.attr("Enabled"), Query.value(true)), |
59 * Query.eq(Query.attr("Owner"), Query.value("Duke"))); |
51 * Query.eq(Query.attr("Owner"), Query.value("Duke"))); |
60 * </pre> |
52 * </pre> |
61 * |
|
62 * <p>Here is how you could construct the same {@code QueryExp} using the |
|
63 * Query Language:</p> |
|
64 * |
|
65 * <pre> |
|
66 * QueryExp query = Query.fromString("Enabled = true and Owner = 'Duke'"); |
|
67 * </pre> |
|
68 * |
|
69 * <p>The principal advantage of the method-chaining approach is that the |
|
70 * compiler will check that the query makes sense. The principal advantage |
|
71 * of the Query Language approach is that it is easier to write and especially |
|
72 * read.</p> |
|
73 * |
|
74 * |
|
75 * <h4 id="ql">Query Language</h4> |
|
76 * |
|
77 * <p>The query language is closely modeled on the WHERE clause of |
|
78 * SQL SELECT statements. The formal specification of the language |
|
79 * appears <a href="#formal-ql">below</a>, but it is probably easier to |
|
80 * understand it with examples such as the following.</p> |
|
81 * |
|
82 * <dl> |
|
83 * <dt>{@code Message = 'OK'} |
|
84 * <dd>Selects MBeans that have a {@code Message} attribute whose value |
|
85 * is the string {@code OK}. |
|
86 * |
|
87 * <dt>{@code FreeSpacePercent < 10} |
|
88 * <dd>Selects MBeans that have a {@code FreeSpacePercent} attribute whose |
|
89 * value is a number less than 10. |
|
90 * |
|
91 * <dt>{@code FreeSpacePercent < 10 and WarningSent = false} |
|
92 * <dd>Selects the same MBeans as the previous example, but they must |
|
93 * also have a boolean attribute {@code WarningSent} whose value |
|
94 * is false. |
|
95 * |
|
96 * <dt>{@code SpaceUsed > TotalSpace * (2.0 / 3.0)} |
|
97 * <dd>Selects MBeans that have {@code SpaceUsed} and {@code TotalSpace} |
|
98 * attributes where the first is more than two-thirds the second. |
|
99 * |
|
100 * <dt>{@code not (FreeSpacePercent between 10 and 90)} |
|
101 * <dd>Selects MBeans that have a {@code FreeSpacePercent} attribute whose |
|
102 * value is not between 10 and 90, inclusive. |
|
103 * |
|
104 * <dt>{@code FreeSpacePercent not between 10 and 90} |
|
105 * <dd>Another way of writing the previous query. |
|
106 * |
|
107 * <dt>{@code Status in ('STOPPED', 'STARTING', 'STARTED')} |
|
108 * <dd>Selects MBeans that have a {@code Status} attribute whose value |
|
109 * is one of those three strings. |
|
110 * |
|
111 * <dt>{@code Message like 'OK: *'} |
|
112 * <dd>Selects MBeans that have a {@code Message} attribute whose value |
|
113 * is a string beginning with {@code "OK: "}. <b>Notice that the |
|
114 * wildcard characters are not the ones that SQL uses.</b> In SQL, |
|
115 * {@code %} means "any sequence of characters" and {@code _} |
|
116 * means "any single character". Here, as in the rest of the JMX API, |
|
117 * those are represented by {@code *} and {@code ?} respectively. |
|
118 * |
|
119 * <dt>{@code instanceof 'javax.management.NotificationBroadcaster'} |
|
120 * <dd>Selects MBeans that are instances of |
|
121 * {@link javax.management.NotificationBroadcaster}, as reported by |
|
122 * {@link javax.management.MBeanServer#isInstanceOf MBeanServer.isInstanceOf}. |
|
123 * |
|
124 * <dt>{@code like 'mydomain:*'} |
|
125 * <dd>Selects MBeans whose {@link ObjectName}s have the domain {@code mydomain}. |
|
126 * |
|
127 * </dl> |
|
128 * |
|
129 * <p>The last two examples do not correspond to valid SQL syntax, but all |
|
130 * the others do.</p> |
|
131 * |
|
132 * <p>The remainder of this description is a formal specification of the |
|
133 * query language.</p> |
|
134 * |
|
135 * |
|
136 * <h4 id="formal-ql">Lexical elements</h4> |
|
137 * |
|
138 * <p>Keywords such as <b>and</b>, <b>like</b>, and <b>between</b> are not |
|
139 * case sensitive. You can write <b>between</b>, <b>BETWEEN</b>, or |
|
140 * <b>BeTwEeN</b> with the same effect.</p> |
|
141 * |
|
142 * <p>On the other hand, attribute names <i>are</i> case sensitive. The |
|
143 * attribute {@code Name} is not the same as the attribute {@code name}.</p> |
|
144 * |
|
145 * <p>To access an attribute whose name, ignoring case, is the same as one of |
|
146 * the keywords {@code not}, {@code instanceof}, {@code like}, {@code true}, |
|
147 * or {@code false}, you can use double quotes, for example {@code "not"}. |
|
148 * Double quotes can also be used to include non-identifier characters in |
|
149 * the name of an attribute, for example {@code "attribute-name-with-hyphens"}. |
|
150 * To include the double quote character in the attribute name, write it |
|
151 * twice. {@code "foo""bar""baz"} represents the attribute called |
|
152 * {@code foo"bar"baz}. |
|
153 * |
|
154 * <p>String constants are written with single quotes like {@code 'this'}. A |
|
155 * single quote within a string constant must be doubled, for example |
|
156 * {@code 'can''t'}.</p> |
|
157 * |
|
158 * <p>Integer constants are written as a sequence of decimal digits, |
|
159 * optionally preceded by a plus or minus sign. An integer constant must be |
|
160 * a valid input to {@link Long#valueOf(String)}.</p> |
|
161 * |
|
162 * <p>Floating-point constants are written using the Java syntax. A |
|
163 * floating-point constant must be a valid input to |
|
164 * {@link Double#valueOf(String)}.</p> |
|
165 * |
|
166 * <p>A boolean constant is either {@code true} or {@code false}, ignoring |
|
167 * case.</p> |
|
168 * |
|
169 * <p>Spaces cannot appear inside identifiers (unless written with double |
|
170 * quotes) or keywords or multi-character tokens such as {@code <=}. Spaces can |
|
171 * appear anywhere else, but are not required except to separate tokens. For |
|
172 * example, the query {@code a < b and 5 = c} could also be written {@code a<b |
|
173 * and 5=c}, but no further spaces can be removed.</p> |
|
174 * |
|
175 * |
|
176 * <h4 id="grammar-ql">Grammar</h4> |
|
177 * |
|
178 * <dl> |
|
179 * <dt id="query">query: |
|
180 * <dd><a href="#andquery">andquery</a> [<b>OR</b> <a href="#query">query</a>] |
|
181 * |
|
182 * <dt id="andquery">andquery: |
|
183 * <dd><a href="#predicate">predicate</a> [<b>AND</b> <a href="#andquery">andquery</a>] |
|
184 * |
|
185 * <dt id="predicate">predicate: |
|
186 * <dd><b>(</b> <a href="#query">query</a> <b>)</b> |<br> |
|
187 * <b>NOT</b> <a href="#predicate">predicate</a> |<br> |
|
188 * <b>INSTANCEOF</b> <a href="#stringvalue">stringvalue</a> |<br> |
|
189 * <b>LIKE</b> <a href="#objectnamepattern">objectnamepattern</a> |<br> |
|
190 * <a href="#value">value</a> <a href="#predrhs">predrhs</a> |
|
191 * |
|
192 * <dt id="predrhs">predrhs: |
|
193 * <dd><a href="#compare">compare</a> <a href="#value">value</a> |<br> |
|
194 * [<b>NOT</b>] <b>BETWEEN</b> <a href="#value">value</a> <b>AND</b> |
|
195 * <a href="#value">value</a> |<br> |
|
196 * [<b>NOT</b>] <b>IN (</b> <a href="#value">value</a> |
|
197 * <a href="#commavalues">commavalues</a> <b>)</b> |<br> |
|
198 * [<b>NOT</b>] <b>LIKE</b> <a href="#stringvalue">stringvalue</a> |
|
199 * |
|
200 * <dt id="commavalues">commavalues: |
|
201 * <dd>[ <b>,</b> <a href="#value">value</a> <a href="#commavalues">commavalues</a> ] |
|
202 * |
|
203 * <dt id="compare">compare: |
|
204 * <dd><b>=</b> | <b><</b> | <b>></b> | |
|
205 * <b><=</b> | <b>>=</b> | <b><></b> | <b>!=</b> |
|
206 * |
|
207 * <dt id="value">value: |
|
208 * <dd><a href="#factor">factor</a> [<a href="#plusorminus">plusorminus</a> |
|
209 * <a href="#value">value</a>] |
|
210 * |
|
211 * <dt id="plusorminus">plusorminus: |
|
212 * <dd><b>+</b> | <b>-</b> |
|
213 * |
|
214 * <dt id="factor">factor: |
|
215 * <dd><a href="#term">term</a> [<a href="#timesordivide">timesordivide</a> |
|
216 * <a href="#factor">factor</a>] |
|
217 * |
|
218 * <dt id="timesordivide">timesordivide: |
|
219 * <dd><b>*</b> | <b>/</b> |
|
220 * |
|
221 * <dt id="term">term: |
|
222 * <dd><a href="#attr">attr</a> | <a href="#literal">literal</a> | |
|
223 * <b>(</b> <a href="#value">value</a> <b>)</b> |
|
224 * |
|
225 * <dt id="attr">attr: |
|
226 * <dd><a href="#name">name</a> [<b>#</b> <a href="#name">name</a>] |
|
227 * |
|
228 * <dt id="name">name: |
|
229 * <dd><a href="#identifier">identifier</a> [<b>.</b><a href="#name">name</a>] |
|
230 * |
|
231 * <dt id="identifier">identifier: |
|
232 * <dd><i>Java-identifier</i> | <i>double-quoted-identifier</i> |
|
233 * |
|
234 * <dt id="literal">literal: |
|
235 * <dd><a href="#booleanlit">booleanlit</a> | <i>longlit</i> | |
|
236 * <i>doublelit</i> | <i>stringlit</i> |
|
237 * |
|
238 * <dt id="booleanlit">booleanlit: |
|
239 * <dd><b>FALSE</b> | <b>TRUE</b> |
|
240 * |
|
241 * <dt id="stringvalue">stringvalue: |
|
242 * <dd><i>stringlit</i> |
|
243 * |
|
244 * <dt id="objectnamepattern">objectnamepattern: |
|
245 * <dd><i>stringlit</i> |
|
246 * |
|
247 * </dl> |
|
248 * |
|
249 * |
|
250 * <h4>Semantics</h4> |
|
251 * |
|
252 * <p>The meaning of the grammar is described in the table below. |
|
253 * This defines a function <i>q</i> that maps a string to a Java object |
|
254 * such as a {@link QueryExp} or a {@link ValueExp}.</p> |
|
255 * |
|
256 * <table border="1" cellpadding="5"> |
|
257 * <tr><th>String <i>s</i></th><th><i>q(s)</th></tr> |
|
258 * |
|
259 * <tr><td><i>query1</i> <b>OR</b> <i>query2</i> |
|
260 * <td>{@link Query#or Query.or}(<i>q(query1)</i>, <i>q(query2)</i>) |
|
261 * |
|
262 * <tr><td><i>query1</i> <b>AND</b> <i>query2</i> |
|
263 * <td>{@link Query#and Query.and}(<i>q(query1)</i>, <i>q(query2)</i>) |
|
264 * |
|
265 * <tr><td><b>(</b> <i>queryOrValue</i> <b>)</b> |
|
266 * <td><i>q(queryOrValue)</i> |
|
267 * |
|
268 * <tr><td><b>NOT</b> <i>query</i> |
|
269 * <td>{@link Query#not Query.not}(<i>q(query)</i>) |
|
270 * |
|
271 * <tr><td><b>INSTANCEOF</b> <i>stringLiteral</i> |
|
272 * <td>{@link Query#isInstanceOf Query.isInstanceOf}(<!-- |
|
273 * -->{@link Query#value(String) Query.value}(<i>q(stringLiteral)</i>)) |
|
274 * |
|
275 * <tr><td><b>LIKE</b> <i>stringLiteral</i> |
|
276 * <td>{@link ObjectName#ObjectName(String) new ObjectName}(<!-- |
|
277 * --><i>q(stringLiteral)</i>) |
|
278 * |
|
279 * <tr><td><i>value1</i> <b>=</b> <i>value2</i> |
|
280 * <td>{@link Query#eq Query.eq}(<i>q(value1)</i>, <i>q(value2)</i>) |
|
281 * |
|
282 * <tr><td><i>value1</i> <b><</b> <i>value2</i> |
|
283 * <td>{@link Query#lt Query.lt}(<i>q(value1)</i>, <i>q(value2)</i>) |
|
284 * |
|
285 * <tr><td><i>value1</i> <b>></b> <i>value2</i> |
|
286 * <td>{@link Query#gt Query.gt}(<i>q(value1)</i>, <i>q(value2)</i>) |
|
287 * |
|
288 * <tr><td><i>value1</i> <b><=</b> <i>value2</i> |
|
289 * <td>{@link Query#leq Query.leq}(<i>q(value1)</i>, <i>q(value2)</i>) |
|
290 * |
|
291 * <tr><td><i>value1</i> <b>>=</b> <i>value2</i> |
|
292 * <td>{@link Query#geq Query.geq}(<i>q(value1)</i>, <i>q(value2)</i>) |
|
293 * |
|
294 * <tr><td><i>value1</i> <b><></b> <i>value2</i> |
|
295 * <td>{@link Query#not Query.not}({@link Query#eq Query.eq}(<!-- |
|
296 * --><i>q(value1)</i>, <i>q(value2)</i>)) |
|
297 * |
|
298 * <tr><td><i>value1</i> <b>!=</b> <i>value2</i> |
|
299 * <td>{@link Query#not Query.not}({@link Query#eq Query.eq}(<!-- |
|
300 * --><i>q(value1)</i>, <i>q(value2)</i>)) |
|
301 * |
|
302 * <tr><td><i>value1</i> <b>BETWEEN</b> <i>value2</i> AND <i>value3</i> |
|
303 * <td>{@link Query#between Query.between}(<i>q(value1)</i>, |
|
304 * <i>q(value2)</i>, <i>q(value3)</i>) |
|
305 * |
|
306 * <tr><td><i>value1</i> <b>NOT BETWEEN</b> <i>value2</i> AND <i>value3</i> |
|
307 * <td>{@link Query#not Query.not}({@link Query#between Query.between}(<!-- |
|
308 * --><i>q(value1)</i>, <i>q(value2)</i>, <i>q(value3)</i>)) |
|
309 * |
|
310 * <tr><td><i>value1</i> <b>IN (</b> <i>value2</i>, <i>value3</i> <b>)</b> |
|
311 * <td>{@link Query#in Query.in}(<i>q(value1)</i>, |
|
312 * <code>new ValueExp[] {</code> |
|
313 * <i>q(value2)</i>, <i>q(value3)</i><code>}</code>) |
|
314 * |
|
315 * <tr><td><i>value1</i> <b>NOT IN (</b> <i>value2</i>, <i>value3</i> <b>)</b> |
|
316 * <td>{@link Query#not Query.not}({@link Query#in Query.in}(<i>q(value1)</i>, |
|
317 * <code>new ValueExp[] {</code> |
|
318 * <i>q(value2)</i>, <i>q(value3)</i><code>}</code>)) |
|
319 * |
|
320 * <tr><td><i>value</i> <b>LIKE</b> <i>stringLiteral</i> |
|
321 * <td>{@link Query#match Query.match}(<i>q(value)</i>, |
|
322 * <i>q(stringLiteral)</i>) |
|
323 * |
|
324 * <tr><td><i>value</i> <b>NOT LIKE</b> <i>stringLiteral</i> |
|
325 * <td>{@link Query#not Query.not}({@link Query#match Query.match}(<i>q(value)</i>, |
|
326 * <i>q(stringLiteral)</i>)) |
|
327 * |
|
328 * <tr><td><i>value1</i> <b>+</b> <i>value2</i> |
|
329 * <td>{@link Query#plus Query.plus}(<i>q(value1)</i>, <i>q(value2)</i>) |
|
330 * |
|
331 * <tr><td><i>value1</i> <b>-</b> <i>value2</i> |
|
332 * <td>{@link Query#minus Query.minus}(<i>q(value1)</i>, <i>q(value2)</i>) |
|
333 * |
|
334 * <tr><td><i>value1</i> <b>*</b> <i>value2</i> |
|
335 * <td>{@link Query#times Query.times}(<i>q(value1)</i>, <i>q(value2)</i>) |
|
336 * |
|
337 * <tr><td><i>value1</i> <b>/</b> <i>value2</i> |
|
338 * <td>{@link Query#div Query.div}(<i>q(value1)</i>, <i>q(value2)</i>) |
|
339 * |
|
340 * <tr><td><i>name</i> |
|
341 * <td>{@link Query#attr(String) Query.attr}(<i>q(name)</i>) |
|
342 * |
|
343 * <tr><td><i>name1<b>#</b>name2</i> |
|
344 * <td>{@link Query#attr(String,String) Query.attr}(<i>q(name1)</i>, |
|
345 * <i>q(name2)</i>) |
|
346 * |
|
347 * <tr><td><b>FALSE</b> |
|
348 * <td>{@link Query#value(boolean) Query.value}(false) |
|
349 * |
|
350 * <tr><td><b>TRUE</b> |
|
351 * <td>{@link Query#value(boolean) Query.value}(true) |
|
352 * |
|
353 * <tr><td><i>decimalLiteral</i> |
|
354 * <td>{@link Query#value(long) Query.value}(<!-- |
|
355 * -->{@link Long#valueOf(String) Long.valueOf}(<i>decimalLiteral</i>)) |
|
356 * |
|
357 * <tr><td><i>floatingPointLiteral</i> |
|
358 * <td>{@link Query#value(double) Query.value}(<!-- |
|
359 * -->{@link Double#valueOf(String) Double.valueOf}(<!-- |
|
360 * --><i>floatingPointLiteral</i>)) |
|
361 * </table> |
|
362 * |
53 * |
363 * @since 1.5 |
54 * @since 1.5 |
364 */ |
55 */ |
365 public class Query extends Object { |
56 public class Query extends Object { |
366 |
57 |
942 public static QueryExp isInstanceOf(StringValueExp classNameValue) { |
638 public static QueryExp isInstanceOf(StringValueExp classNameValue) { |
943 return new InstanceOfQueryExp(classNameValue); |
639 return new InstanceOfQueryExp(classNameValue); |
944 } |
640 } |
945 |
641 |
946 /** |
642 /** |
947 * <p>Return a string representation of the given query. The string |
|
948 * returned by this method can be converted back into an equivalent |
|
949 * query using {@link #fromString fromString}.</p> |
|
950 * |
|
951 * <p>(Two queries are equivalent if they produce the same result in |
|
952 * all cases. Equivalent queries are not necessarily identical: |
|
953 * for example the queries {@code Query.lt(Query.attr("A"), Query.attr("B"))} |
|
954 * and {@code Query.not(Query.ge(Query.attr("A"), Query.attr("B")))} are |
|
955 * equivalent but not identical.)</p> |
|
956 * |
|
957 * <p>The string returned by this method is only guaranteed to be converted |
|
958 * back into an equivalent query if {@code query} was constructed, or |
|
959 * could have been constructed, using the methods of this class. |
|
960 * If you make a custom query {@code myQuery} by implementing |
|
961 * {@link QueryExp} yourself then the result of |
|
962 * {@code Query.toString(myQuery)} is unspecified.</p> |
|
963 * |
|
964 * @param query the query to convert. If it is null, the result will |
|
965 * also be null. |
|
966 * @return the string representation of the query, or null if the |
|
967 * query is null. |
|
968 * |
|
969 * @since 1.7 |
|
970 */ |
|
971 public static String toString(QueryExp query) { |
|
972 if (query == null) |
|
973 return null; |
|
974 |
|
975 // This is ugly. At one stage we had a non-public class called |
|
976 // ToQueryString with the toQueryString() method, and every class |
|
977 // mentioned here inherited from that class. But that interfered |
|
978 // with serialization of custom subclasses of e.g. QueryEval. Even |
|
979 // though we could make it work by adding a public constructor to this |
|
980 // non-public class, that seemed fragile because according to the |
|
981 // serialization spec it shouldn't work. If only non-public interfaces |
|
982 // could have non-public methods. |
|
983 if (query instanceof ObjectName) |
|
984 return ((ObjectName) query).toQueryString(); |
|
985 if (query instanceof QueryEval) |
|
986 return ((QueryEval) query).toQueryString(); |
|
987 |
|
988 return query.toString(); |
|
989 } |
|
990 |
|
991 /** |
|
992 * <p>Produce a query from the given string. The query returned |
|
993 * by this method can be converted back into a string using |
|
994 * {@link #toString(QueryExp) toString}. The resultant string will |
|
995 * not necessarily be equal to {@code s}.</p> |
|
996 * |
|
997 * @param s the string to convert. |
|
998 * |
|
999 * @return a {@code QueryExp} derived by parsing the string, or |
|
1000 * null if the string is null. |
|
1001 * |
|
1002 * @throws IllegalArgumentException if the string is not a valid |
|
1003 * query string. |
|
1004 * |
|
1005 * @since 1.7 |
|
1006 */ |
|
1007 public static QueryExp fromString(String s) { |
|
1008 if (s == null) |
|
1009 return null; |
|
1010 return new QueryParser(s).parseQuery(); |
|
1011 } |
|
1012 |
|
1013 /** |
|
1014 * Utility method to escape strings used with |
643 * Utility method to escape strings used with |
1015 * Query.{initial|any|final}SubString() methods. |
644 * Query.{initial|any|final}SubString() methods. |
1016 */ |
645 */ |
1017 private static String escapeString(String s) { |
646 private static String escapeString(String s) { |
1018 if (s == null) |
647 if (s == null) |