6692027: Custom subclasses of QueryEval don't serialize
authoremcmanus
Tue, 22 Apr 2008 18:58:40 +0200
changeset 471 422ff0db58d3
parent 407 c28cb4692634
child 484 e13b7add5742
6692027: Custom subclasses of QueryEval don't serialize Summary: Remove non-public superclass of QueryEval Reviewed-by: dfuchs
jdk/src/share/classes/javax/management/AndQueryExp.java
jdk/src/share/classes/javax/management/BetweenQueryExp.java
jdk/src/share/classes/javax/management/BinaryRelQueryExp.java
jdk/src/share/classes/javax/management/NotQueryExp.java
jdk/src/share/classes/javax/management/ObjectName.java
jdk/src/share/classes/javax/management/OrQueryExp.java
jdk/src/share/classes/javax/management/Query.java
jdk/src/share/classes/javax/management/QueryEval.java
jdk/test/javax/management/query/CustomQueryTest.java
--- 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);
+        }
+    }
+}