JDK-8234305 : ClassCastException when calling FlightRecorderMXBean#getRecordings()
  • Type: CSR
  • Component: hotspot
  • Sub-Component: jfr
  • Priority: P3
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 15
  • Submitted: 2019-11-17
  • Updated: 2020-06-17
  • Resolved: 2019-12-17
Related Reports
CSR :  
Description
Summary
-------

Change the specification of the jdk.management.jfr.RecordingInfo::from(CompositeData) method so it states that the key "toDisk" (instead of "disk") should be used for deserialization.

Problem
-------
The FlightRecorderMXBean::getRecordings() method returns a list of RecordingInfo objects containing information about available recordings. A recording can be persisted to disk or to memory, and users can invoke the RecordingInfo::isToDisk() method to find out which. 

When a RecordingInfo object is serialized as a CompositeData object, the key is derived from the method name and becomes "toDisk". The deserialization method RecordingInfo::from method on the other hand assumes the key is "disk", which means an InvalidKeyException is thrown. The deserialization method also assumes that the recording id is of type int, when it is a long, leading to a ClassCastException on the client. This happens before the InvalidKeyException is reached.

Solution
--------

Change the implementation of the RecordingInfo class so that the recording id is deserialized as a long and use the key "toDisk" instead of "disk". At the same time, update the javadoc for the RecordingInfo::from method so it states that the key "toDisk" should be used.

An alternative approach would be to change the implementation to match the javadoc, but that would cause problems for clients, such as JDK Mission Control, that already operates against the CompositeData and assumes the key is "toDisk". 

Specification
-------------

src/jdk.management.jfr/share/classes/jdk/management/jfr/RecordingInfo.java
```
@@ -51,7 +51,7 @@
     private final String state;
     private final boolean dumpOnExit;
     private final long size;
-    private final boolean disk;
+    private final boolean toDisk;
     private final long maxAge;
     private final long maxSize;
     private final long startTime;
@@ -67,7 +67,7 @@
         state = recording.getState().toString();
         dumpOnExit = recording.getDumpOnExit();
         size = recording.getSize();
-        disk = recording.isToDisk();
+        toDisk = recording.isToDisk();
 
         Duration d = recording.getMaxAge();
         if (d == null) {
@@ -87,12 +87,17 @@
     }
 
     private RecordingInfo(CompositeData cd) {
-        id = (int) cd.get("id");
+        id = (long) cd.get("id");
         name = (String) cd.get("name");
         state = (String) cd.get("state");
         dumpOnExit = (boolean) cd.get("dumpOnExit");
         size = (long) cd.get("size");
-        disk = (boolean) cd.get("disk");
+        if(cd.containsKey("toDisk")){
+            toDisk = (boolean) cd.get("toDisk");
+        } else {
+            // Before JDK-8219904 was fixed, the element name was disk, so for compatibility
+            toDisk = (boolean) cd.get("disk");
+        }
         maxAge = (Long) cd.get("maxAge");
         maxSize = (Long) cd.get("maxSize");
         startTime = (Long) cd.get("startTime");
@@ -290,7 +295,7 @@
      * @return {@code true} if recording is to disk, {@code false} otherwise
      */
     public boolean isToDisk() {
-        return disk;
+        return toDisk;
     }
 
     /**
@@ -342,7 +347,7 @@
      * <td>{@code Long}</td>
      * </tr>
      * <tr>
-     * <th scope="row">disk</th>
+     * <th scope="row">toDisk</th>
      * <td>{@code Boolean}</td>
      * </tr>
      * <tr>
```
For convenience the current webrev can be browsed here:
http://cr.openjdk.java.net/~cito/JDK-8219904/webrev.03/
Comments
Approving for JDK 15. If you want to get this into JDK 14, currently in ramp down 1, please follow the appropriate procedures for that release and update the fixVersion of the CSR.
17-12-2019