1 /* |
|
2 * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. |
|
8 * |
|
9 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
12 * version 2 for more details (a copy is included in the LICENSE file that |
|
13 * accompanied this code). |
|
14 * |
|
15 * You should have received a copy of the GNU General Public License version |
|
16 * 2 along with this work; if not, write to the Free Software Foundation, |
|
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
18 * |
|
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
|
20 * CA 95054 USA or visit www.sun.com if you need additional information or |
|
21 * have any questions. |
|
22 */ |
|
23 |
|
24 /* |
|
25 * @test QueryNotifFilterTest |
|
26 * @bug 6610917 |
|
27 * @summary Test the QueryNotificationFilter class |
|
28 * @author Eamonn McManus |
|
29 */ |
|
30 |
|
31 import java.util.Arrays; |
|
32 import java.util.Collections; |
|
33 import java.util.HashSet; |
|
34 import java.util.List; |
|
35 import java.util.Set; |
|
36 import javax.management.Attribute; |
|
37 import javax.management.AttributeChangeNotification; |
|
38 import javax.management.MBeanAttributeInfo; |
|
39 import javax.management.MBeanInfo; |
|
40 import javax.management.MBeanServer; |
|
41 import javax.management.MalformedObjectNameException; |
|
42 import javax.management.Notification; |
|
43 import javax.management.NotificationFilter; |
|
44 import javax.management.ObjectInstance; |
|
45 import javax.management.ObjectName; |
|
46 import javax.management.Query; |
|
47 import javax.management.QueryEval; |
|
48 import javax.management.QueryExp; |
|
49 import javax.management.QueryNotificationFilter; |
|
50 |
|
51 public class QueryNotifFilterTest { |
|
52 private static class Case { |
|
53 final Notification notif; |
|
54 final QueryExp query; |
|
55 final boolean expect; |
|
56 final Class<? extends Notification> notifClass; |
|
57 Case(Notification notif, String query, boolean expect) { |
|
58 this(notif, query, notif.getClass(), expect); |
|
59 } |
|
60 Case(Notification notif, String query, |
|
61 Class<? extends Notification> notifClass, boolean expect) { |
|
62 this(notif, Query.fromString(query), notifClass, expect); |
|
63 } |
|
64 Case(Notification notif, QueryExp query, boolean expect) { |
|
65 this(notif, query, notif.getClass(), expect); |
|
66 } |
|
67 Case(Notification notif, QueryExp query, |
|
68 Class<? extends Notification> notifClass, boolean expect) { |
|
69 this.notif = notif; |
|
70 this.query = query; |
|
71 this.expect = expect; |
|
72 this.notifClass = notifClass; |
|
73 } |
|
74 } |
|
75 |
|
76 /* In principle users can create their own implementations of QueryExp |
|
77 * and use them with QueryNotificationFilter. If they do so, then |
|
78 * they can call any MBeanServer method. Not all of those methods |
|
79 * will work with the special MBeanServer we concoct to analyze a |
|
80 * Notification, but some will, including some that are not called |
|
81 * by the standard queries. So we check each of those cases too. |
|
82 */ |
|
83 private static class ExoticCase { |
|
84 final Notification trueNotif; |
|
85 final Notification falseNotif; |
|
86 final QueryExp query; |
|
87 ExoticCase(Notification trueNotif, Notification falseNotif, QueryExp query) { |
|
88 this.trueNotif = trueNotif; |
|
89 this.falseNotif = falseNotif; |
|
90 this.query = query; |
|
91 } |
|
92 } |
|
93 |
|
94 private static abstract class ExoticQuery |
|
95 extends QueryEval implements QueryExp { |
|
96 private final String queryString; |
|
97 ExoticQuery(String queryString) { |
|
98 this.queryString = queryString; |
|
99 } |
|
100 abstract boolean apply(MBeanServer mbs, ObjectName name) throws Exception; |
|
101 //@Override - doesn't override in JDK5 |
|
102 public boolean apply(ObjectName name) { |
|
103 try { |
|
104 return apply(getMBeanServer(), name); |
|
105 } catch (Exception e) { |
|
106 e.printStackTrace(System.out); |
|
107 return false; |
|
108 } |
|
109 } |
|
110 @Override |
|
111 public String toString() { |
|
112 return queryString; |
|
113 } |
|
114 } |
|
115 |
|
116 private static ObjectName makeObjectName(String s) { |
|
117 try { |
|
118 return new ObjectName(s); |
|
119 } catch (MalformedObjectNameException e) { |
|
120 throw new RuntimeException(e); |
|
121 } |
|
122 } |
|
123 |
|
124 public static class CustomNotification extends Notification { |
|
125 public CustomNotification(String type, Object source, long seqNo) { |
|
126 super(type, source, seqNo); |
|
127 } |
|
128 |
|
129 public String getName() { |
|
130 return "claude"; |
|
131 } |
|
132 |
|
133 public boolean isInteresting() { |
|
134 return true; |
|
135 } |
|
136 } |
|
137 |
|
138 private static final Notification simpleNotif = |
|
139 new Notification("mytype", "source", 0L); |
|
140 private static final Notification attrChangeNotif = |
|
141 new AttributeChangeNotification( |
|
142 "x", 0L, 0L, "msg", "AttrName", "int", 2, 3); |
|
143 private static final ObjectName testObjectName = makeObjectName("a:b=c"); |
|
144 private static final Notification sourcedNotif = |
|
145 new Notification("mytype", testObjectName, 0L); |
|
146 private static final Notification customNotif = |
|
147 new CustomNotification("mytype", testObjectName, 0L); |
|
148 |
|
149 private static final Case[] testCases = { |
|
150 new Case(simpleNotif, "Type = 'mytype'", true), |
|
151 new Case(simpleNotif, "Type = 'mytype'", |
|
152 Notification.class, true), |
|
153 new Case(simpleNotif, "Type = 'mytype'", |
|
154 AttributeChangeNotification.class, false), |
|
155 new Case(simpleNotif, "Type != 'mytype'", false), |
|
156 new Case(simpleNotif, "Type = 'somethingelse'", false), |
|
157 new Case(attrChangeNotif, "AttributeName = 'AttrName'", true), |
|
158 new Case(attrChangeNotif, |
|
159 "instanceof 'javax.management.AttributeChangeNotification'", |
|
160 true), |
|
161 new Case(attrChangeNotif, |
|
162 "instanceof 'javax.management.Notification'", |
|
163 true), |
|
164 new Case(attrChangeNotif, |
|
165 "instanceof 'javax.management.relation.MBeanServerNotification'", |
|
166 false), |
|
167 new Case(attrChangeNotif, |
|
168 "class = 'javax.management.AttributeChangeNotification'", |
|
169 true), |
|
170 new Case(attrChangeNotif, |
|
171 "javax.management.AttributeChangeNotification#AttributeName = 'AttrName'", |
|
172 true), |
|
173 new Case(sourcedNotif, |
|
174 testObjectName, |
|
175 true), |
|
176 new Case(sourcedNotif, |
|
177 makeObjectName("a*:b=*"), |
|
178 true), |
|
179 new Case(sourcedNotif, |
|
180 makeObjectName("a*:c=*"), |
|
181 false), |
|
182 new Case(customNotif, "Name = 'claude'", true), |
|
183 new Case(customNotif, "Name = 'tiddly'", false), |
|
184 new Case(customNotif, "Interesting = true", true), |
|
185 new Case(customNotif, "Interesting = false", false), |
|
186 }; |
|
187 |
|
188 private static final ExoticCase[] exoticTestCases = { |
|
189 new ExoticCase( |
|
190 simpleNotif, new Notification("notmytype", "source", 0L), |
|
191 new ExoticQuery("getAttributes") { |
|
192 boolean apply(MBeanServer mbs, ObjectName name) |
|
193 throws Exception { |
|
194 List<Attribute> attrs = mbs.getAttributes( |
|
195 name, new String[] {"Type", "Source"}).asList(); |
|
196 return (attrs.get(0).equals(new Attribute("Type", "mytype")) && |
|
197 attrs.get(1).equals(new Attribute("Source", "source"))); |
|
198 } |
|
199 }), |
|
200 new ExoticCase( |
|
201 new Notification("mytype", "source", 0L) {}, |
|
202 simpleNotif, |
|
203 new ExoticQuery("getClassLoaderFor") { |
|
204 boolean apply(MBeanServer mbs, ObjectName name) |
|
205 throws Exception { |
|
206 return (mbs.getClassLoaderFor(name) == |
|
207 this.getClass().getClassLoader()); |
|
208 } |
|
209 }), |
|
210 new ExoticCase( |
|
211 sourcedNotif, simpleNotif, |
|
212 new ExoticQuery("getDomains") { |
|
213 boolean apply(MBeanServer mbs, ObjectName name) |
|
214 throws Exception { |
|
215 return Arrays.equals(mbs.getDomains(), |
|
216 new String[] {testObjectName.getDomain()}); |
|
217 } |
|
218 }), |
|
219 new ExoticCase( |
|
220 simpleNotif, attrChangeNotif, |
|
221 new ExoticQuery("getMBeanInfo") { |
|
222 boolean apply(MBeanServer mbs, ObjectName name) |
|
223 throws Exception { |
|
224 MBeanInfo mbi = mbs.getMBeanInfo(name); |
|
225 // If we ever add a constructor to Notification then |
|
226 // we will have to change the 4 below. |
|
227 if (mbi.getOperations().length > 0 || |
|
228 mbi.getConstructors().length != 4 || |
|
229 mbi.getNotifications().length > 0) |
|
230 return false; |
|
231 Set<String> expect = new HashSet<String>( |
|
232 Arrays.asList( |
|
233 "Class", "Message", "SequenceNumber", "Source", |
|
234 "TimeStamp", "Type", "UserData")); |
|
235 Set<String> actual = new HashSet<String>(); |
|
236 for (MBeanAttributeInfo mbai : mbi.getAttributes()) |
|
237 actual.add(mbai.getName()); |
|
238 return actual.equals(expect); |
|
239 } |
|
240 }), |
|
241 new ExoticCase( |
|
242 simpleNotif, attrChangeNotif, |
|
243 new ExoticQuery("getObjectInstance") { |
|
244 boolean apply(MBeanServer mbs, ObjectName name) |
|
245 throws Exception { |
|
246 ObjectInstance oi = mbs.getObjectInstance(name); |
|
247 return oi.getClassName().equals(Notification.class.getName()); |
|
248 } |
|
249 }), |
|
250 new ExoticCase( |
|
251 sourcedNotif, simpleNotif, |
|
252 new ExoticQuery("queryNames") { |
|
253 boolean apply(MBeanServer mbs, ObjectName name) |
|
254 throws Exception { |
|
255 Set<ObjectName> names = mbs.queryNames(null, |
|
256 Query.eq(Query.attr("Type"), Query.value("mytype"))); |
|
257 return names.equals(Collections.singleton(testObjectName)); |
|
258 } |
|
259 }), |
|
260 new ExoticCase( |
|
261 sourcedNotif, simpleNotif, |
|
262 new ExoticQuery("queryMBeans") { |
|
263 boolean apply(MBeanServer mbs, ObjectName name) |
|
264 throws Exception { |
|
265 Set<ObjectInstance> insts = mbs.queryMBeans(null, |
|
266 Query.eq(Query.attr("Type"), Query.value("mytype"))); |
|
267 if (insts.size() != 1) |
|
268 return false; |
|
269 ObjectInstance inst = insts.iterator().next(); |
|
270 return (inst.getObjectName().equals(testObjectName) && |
|
271 inst.getClassName().equals(Notification.class.getName())); |
|
272 } |
|
273 }), |
|
274 }; |
|
275 |
|
276 private static enum Test { |
|
277 QUERY_EXP("query"), STRING("string"), STRING_PLUS_CLASS("string with class"); |
|
278 private final String name; |
|
279 Test(String name) { |
|
280 this.name = name; |
|
281 } |
|
282 @Override |
|
283 public String toString() { |
|
284 return name; |
|
285 } |
|
286 } |
|
287 |
|
288 public static void main(String[] args) throws Exception { |
|
289 boolean allok = true; |
|
290 for (Case testCase : testCases) { |
|
291 for (Test test : Test.values()) { |
|
292 QueryNotificationFilter nf; |
|
293 String queryString; |
|
294 switch (test) { |
|
295 case QUERY_EXP: { |
|
296 QueryExp inst = Query.isInstanceOf( |
|
297 Query.value(testCase.notifClass.getName())); |
|
298 QueryExp and = Query.and(inst, testCase.query); |
|
299 queryString = Query.toString(and); |
|
300 nf = new QueryNotificationFilter(and); |
|
301 break; |
|
302 } |
|
303 case STRING: { |
|
304 String s = "instanceof '" + testCase.notifClass.getName() + "'"; |
|
305 queryString = s + " and " + Query.toString(testCase.query); |
|
306 nf = new QueryNotificationFilter(queryString); |
|
307 break; |
|
308 } |
|
309 case STRING_PLUS_CLASS: |
|
310 queryString = null; |
|
311 nf = new QueryNotificationFilter( |
|
312 testCase.notifClass, Query.toString(testCase.query)); |
|
313 break; |
|
314 default: |
|
315 throw new AssertionError(); |
|
316 } |
|
317 boolean accept = nf.isNotificationEnabled(testCase.notif); |
|
318 if (queryString != null) { |
|
319 queryString = Query.toString(Query.fromString(queryString)); |
|
320 if (!queryString.equals(Query.toString(nf.getQuery()))) { |
|
321 System.out.println("FAIL: query string mismatch: expected " + |
|
322 "\"" + queryString + "\", got \"" + |
|
323 Query.toString(nf.getQuery())); |
|
324 allok = false; |
|
325 } |
|
326 } |
|
327 boolean ok = (accept == testCase.expect); |
|
328 System.out.println((ok ? "pass" : "FAIL") + ": " + |
|
329 testCase.query + " (" + test + ")"); |
|
330 allok &= ok; |
|
331 } |
|
332 } |
|
333 for (ExoticCase testCase : exoticTestCases) { |
|
334 NotificationFilter nf = new QueryNotificationFilter(testCase.query); |
|
335 for (boolean expect : new boolean[] {true, false}) { |
|
336 Notification n = expect ? testCase.trueNotif : testCase.falseNotif; |
|
337 boolean accept = nf.isNotificationEnabled(n); |
|
338 boolean ok = (accept == expect); |
|
339 System.out.println((ok ? "pass" : "FAIL") + ": " + |
|
340 testCase.query + ": " + n); |
|
341 allok &= ok; |
|
342 } |
|
343 } |
|
344 if (!allok) |
|
345 throw new Exception("TEST FAILED"); |
|
346 } |
|
347 } |
|