6692027: Custom subclasses of QueryEval don't serialize
Summary: Remove non-public superclass of QueryEval
Reviewed-by: dfuchs
--- a/jdk/src/share/classes/javax/management/AndQueryExp.java Mon Apr 21 11:24:04 2008 -0400
+++ b/jdk/src/share/classes/javax/management/AndQueryExp.java Tue Apr 22 18:58:40 2008 +0200
@@ -100,12 +100,13 @@
/**
* Returns a string representation of this AndQueryExp
*/
- public String toString() {
- return "(" + exp1 + ") and (" + exp2 + ")";
- }
+ @Override
+ public String toString() {
+ return "(" + exp1 + ") and (" + exp2 + ")";
+ }
- @Override
- String toQueryString() {
+ @Override
+ String toQueryString() {
// Parentheses are only added if needed to disambiguate.
return parens(exp1) + " and " + parens(exp2);
}
--- a/jdk/src/share/classes/javax/management/BetweenQueryExp.java Mon Apr 21 11:24:04 2008 -0400
+++ b/jdk/src/share/classes/javax/management/BetweenQueryExp.java Tue Apr 22 18:58:40 2008 +0200
@@ -135,6 +135,7 @@
/**
* Returns the string representing the object.
*/
+ @Override
public String toString() {
return "(" + exp1 + ") between (" + exp2 + ") and (" + exp3 + ")";
}
--- a/jdk/src/share/classes/javax/management/BinaryRelQueryExp.java Mon Apr 21 11:24:04 2008 -0400
+++ b/jdk/src/share/classes/javax/management/BinaryRelQueryExp.java Tue Apr 22 18:58:40 2008 +0200
@@ -187,11 +187,11 @@
/**
* Returns the string representing the object.
*/
+ @Override
public String toString() {
return "(" + exp1 + ") " + relOpString() + " (" + exp2 + ")";
}
- @Override
String toQueryString() {
return exp1 + " " + relOpString() + " " + exp2;
}
--- a/jdk/src/share/classes/javax/management/NotQueryExp.java Mon Apr 21 11:24:04 2008 -0400
+++ b/jdk/src/share/classes/javax/management/NotQueryExp.java Tue Apr 22 18:58:40 2008 +0200
@@ -91,7 +91,6 @@
return "not (" + exp + ")";
}
- @Override
String toQueryString() {
return "not (" + Query.toString(exp) + ")";
}
--- a/jdk/src/share/classes/javax/management/ObjectName.java Mon Apr 21 11:24:04 2008 -0400
+++ b/jdk/src/share/classes/javax/management/ObjectName.java Tue Apr 22 18:58:40 2008 +0200
@@ -223,8 +223,7 @@
* @since 1.5
*/
@SuppressWarnings("serial") // don't complain serialVersionUID not constant
-public class ObjectName extends ToQueryString
- implements Comparable<ObjectName>, QueryExp {
+public class ObjectName implements Comparable<ObjectName>, QueryExp {
/**
* A structure recording property structure and
@@ -1781,7 +1780,6 @@
return getSerializedNameString();
}
- @Override
String toQueryString() {
return "LIKE " + Query.value(toString());
}
--- a/jdk/src/share/classes/javax/management/OrQueryExp.java Mon Apr 21 11:24:04 2008 -0400
+++ b/jdk/src/share/classes/javax/management/OrQueryExp.java Tue Apr 22 18:58:40 2008 +0200
@@ -100,6 +100,7 @@
/**
* Returns a string representation of this OrQueryExp
*/
+ @Override
public String toString() {
return "(" + exp1 + ") or (" + exp2 + ")";
}
--- a/jdk/src/share/classes/javax/management/Query.java Mon Apr 21 11:24:04 2008 -0400
+++ b/jdk/src/share/classes/javax/management/Query.java Tue Apr 22 18:58:40 2008 +0200
@@ -979,8 +979,18 @@
if (query == null)
return null;
- if (query instanceof ToQueryString)
- return ((ToQueryString) query).toQueryString();
+ // This is ugly. At one stage we had a non-public class called
+ // ToQueryString with the toQueryString() method, and every class
+ // mentioned here inherited from that class. But that interfered
+ // with serialization of custom subclasses of e.g. QueryEval. Even
+ // though we could make it work by adding a public constructor to this
+ // non-public class, that seemed fragile because according to the
+ // serialization spec it shouldn't work. If only non-public interfaces
+ // could have non-public methods.
+ if (query instanceof ObjectName)
+ return ((ObjectName) query).toQueryString();
+ if (query instanceof QueryEval)
+ return ((QueryEval) query).toQueryString();
return query.toString();
}
--- a/jdk/src/share/classes/javax/management/QueryEval.java Mon Apr 21 11:24:04 2008 -0400
+++ b/jdk/src/share/classes/javax/management/QueryEval.java Tue Apr 22 18:58:40 2008 +0200
@@ -25,20 +25,15 @@
package javax.management;
-
// java import
import java.io.Serializable;
-// RI import
-import javax.management.MBeanServer;
-
-
/**
* Allows a query to be performed in the context of a specific MBean server.
*
* @since 1.5
*/
-public abstract class QueryEval extends ToQueryString implements Serializable {
+public abstract class QueryEval implements Serializable {
/* Serial version */
private static final long serialVersionUID = 2675899265640874796L;
@@ -80,4 +75,10 @@
public static MBeanServer getMBeanServer() {
return server.get();
}
+
+ // Subclasses in this package can override this method to return a different
+ // string.
+ String toQueryString() {
+ return toString();
+ }
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/query/CustomQueryTest.java Tue Apr 22 18:58:40 2008 +0200
@@ -0,0 +1,116 @@
+/*
+ * 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
+ * @bug 6692027
+ * @summary Check that custom subclasses of QueryEval can be serialized.
+ * @author Eamonn McManus
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.management.ManagementFactory;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.QueryEval;
+import javax.management.QueryExp;
+
+public class CustomQueryTest {
+ public static interface CountMBean {
+ public int getCount();
+ public void increment();
+ }
+
+ public static class Count implements CountMBean {
+ private AtomicInteger count = new AtomicInteger();
+
+ public int getCount() {
+ return count.get();
+ }
+
+ public void increment() {
+ count.incrementAndGet();
+ }
+
+ }
+
+ public static final ObjectName countName;
+ static {
+ try {
+ countName = new ObjectName("d:type=Count");
+ } catch (MalformedObjectNameException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ /* A query that calls the increment method of the Count MBean every time
+ * it is evaluated. If there is no ObjectName filter, the query will be
+ * evaluated for every MBean in the MBean Server, so the count will be
+ * incremented by the number of MBeans.
+ */
+ public static class IncrQuery extends QueryEval implements QueryExp {
+ public boolean apply(ObjectName name) {
+ try {
+ getMBeanServer().invoke(countName, "increment", null, null);
+ return true;
+ } catch (Throwable t) {
+ t.printStackTrace();
+ System.exit(1);
+ throw new AssertionError(); // not reached
+ }
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+ mbs.registerMBean(new Count(), countName);
+ int mbeanCount = mbs.getMBeanCount();
+ QueryExp query = new IncrQuery();
+ Set<ObjectName> names = mbs.queryNames(null, query);
+ assertEquals(mbeanCount, names.size());
+ assertEquals(mbeanCount, mbs.getAttribute(countName, "Count"));
+ 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();
+ names = mbs.queryNames(null, query);
+ assertEquals(mbeanCount * 2, mbs.getAttribute(countName, "Count"));
+ }
+
+ private static void assertEquals(Object expected, Object actual)
+ throws Exception {
+ if (!expected.equals(actual)) {
+ String failure = "FAILED: expected " + expected + ", got " + actual;
+ throw new Exception(failure);
+ }
+ }
+}