1 /* 2 * Copyright (c) 2016, 2019, Oracle and/or its affiliates. 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.management.jfr; 27 28 import java.time.Duration; 29 import java.time.Instant; 30 import java.util.LinkedHashMap; 31 import java.util.List; 32 import java.util.Map; 33 34 import javax.management.openmbean.CompositeData; 35 import javax.management.openmbean.TabularData; 36 37 import jdk.jfr.Recording; 38 import jdk.jfr.RecordingState; 39 import jdk.jfr.internal.management.ManagementSupport; 40 41 /** 42 * Management representation of a {@code Recording}. 43 * 44 * @see Recording 45 * 46 * @since 9 47 */ 48 public final class RecordingInfo { 49 private final long id; 50 private final String name; 51 private final String state; 52 private final boolean dumpOnExit; 53 private final long size; 54 private final boolean toDisk; 55 private final long maxAge; 56 private final long maxSize; 57 private final long startTime; 58 private final long stopTime; 59 private final String destination; 60 private final long durationInSeconds; 61 private final Map<String, String> settings; 62 63 // package private 64 RecordingInfo(Recording recording) { 65 id = recording.getId(); 66 name = recording.getName(); 67 state = recording.getState().toString(); 68 dumpOnExit = recording.getDumpOnExit(); 69 size = recording.getSize(); 70 toDisk = recording.isToDisk(); 71 72 Duration d = recording.getMaxAge(); 73 if (d == null) { 74 maxAge = 0; 75 } else { 76 maxAge = d.getSeconds(); 77 } 78 maxSize = recording.getMaxSize(); 79 Instant s = recording.getStartTime(); 80 startTime = s == null ? 0L : s.toEpochMilli(); 81 Instant st = recording.getStopTime(); 82 stopTime = st == null ? 0L : st.toEpochMilli(); 83 destination = ManagementSupport.getDestinationOriginalText(recording); 84 Duration duration = recording.getDuration(); 85 durationInSeconds = duration == null ? 0 : duration.getSeconds(); 86 settings = recording.getSettings(); 87 } 88 89 private RecordingInfo(CompositeData cd) { 90 id = (long) cd.get("id"); 91 name = (String) cd.get("name"); 92 state = (String) cd.get("state"); 93 dumpOnExit = (boolean) cd.get("dumpOnExit"); 94 size = (long) cd.get("size"); 95 if(cd.containsKey("toDisk")){ 96 toDisk = (boolean) cd.get("toDisk"); 97 } else { 98 // Before JDK-8219904 was fixed, the element name was disk, so for compatibility 99 toDisk = (boolean) cd.get("disk"); 100 } 101 maxAge = (Long) cd.get("maxAge"); 102 maxSize = (Long) cd.get("maxSize"); 103 startTime = (Long) cd.get("startTime"); 104 stopTime = (Long) cd.get("stopTime"); 105 destination = (String) cd.get("destination"); 106 durationInSeconds = (long) cd.get("duration"); 107 settings = new LinkedHashMap<>(); 108 Object map = cd.get("settings"); 109 if (map instanceof TabularData) { 110 TabularData td = (TabularData) map; 111 List<String> keyNames = td.getTabularType().getIndexNames(); 112 int size = keyNames.size(); 113 for (Object keys : td.keySet()) { 114 Object[] keyValues = ((List<?>) keys).toArray(); 115 for (int i = 0; i < size; i++) { 116 String key = keyNames.get(i); 117 Object value = keyValues[i]; 118 if (value instanceof String) { 119 settings.put(key, (String) value); 120 } 121 } 122 } 123 } 124 } 125 126 /** 127 * Returns the name of the recording associated with this 128 * {@code RecordingInfo}. 129 * 130 * @return the recording name, not {@code null} 131 * 132 * @see Recording#getName() 133 */ 134 public String getName() { 135 return name; 136 } 137 138 /** 139 * Returns the unique ID for the recording associated with this 140 * {@code RecordingInfo}. 141 * 142 * @return the recording ID 143 * 144 * @see Recording#getId() 145 */ 146 public long getId() { 147 return id; 148 } 149 150 /** 151 * Returns if the recording associated with this {@code RecordingInfo} 152 * should be dumped to file when the JVM exits. 153 * 154 * @return {@code true} if recording should be dumped on exit, {@code false} 155 * otherwise 156 * 157 * @see Recording#getDumpOnExit() 158 */ 159 public boolean getDumpOnExit() { 160 return dumpOnExit; 161 } 162 163 /** 164 * Returns how many seconds data should be kept on disk, or {@code 0} if 165 * data is to be kept forever. 166 * <p> 167 * In-memory recordings are not affected by maximum age. 168 * 169 * @see Recording#getMaxAge() 170 * @see Recording#setToDisk(boolean) 171 * @return how long data should be kept on disk, measured in seconds 172 * 173 */ 174 public long getMaxAge() { 175 return maxAge; 176 } 177 178 /** 179 * Returns the amount of data, measured in bytes, the recording associated 180 * with this {@code RecordingInfo}, should be kept on disk, before it's 181 * rotated away, or {@code 0} if data is to be kept indefinitely. 182 * <p> 183 * In-memory recordings are not affected by maximum size. 184 * 185 * @return the amount of data should be kept on disk, in bytes 186 * 187 * @see Recording#setToDisk(boolean) 188 * @see Recording#getMaxSize() 189 */ 190 public long getMaxSize() { 191 return maxSize; 192 } 193 194 /** 195 * Returns a {@code String} representation of state of the recording 196 * associated with this {@code RecordingInfo}. 197 * <p> 198 * Valid return values are {@code "NEW"}, {@code "DELAYED"}, {@code "STARTING"}, 199 * {@code "RUNNING"}, {@code "STOPPING"}, {@code "STOPPED"} and {@code "CLOSED"}. 200 * 201 * @return the recording state, not {@code null} 202 * 203 * @see RecordingState#toString() 204 * @see Recording#getState() 205 */ 206 public String getState() { 207 return state; 208 } 209 210 /** 211 * Returns start time of the recording associated with this 212 * {@code RecordingInfo}, measured as ms since epoch, or {@code null} if the 213 * recording hasn't started. 214 * 215 * @return the start time of the recording, or {@code null} if the recording 216 * hasn't started 217 * 218 * @see Recording#getStartTime() 219 */ 220 public long getStartTime() { 221 return startTime; 222 } 223 224 /** 225 * Returns the actual or expected stop time of the recording associated with 226 * this {@code RecordingInfo}, measured as ms since epoch, or {@code null} 227 * if the expected or actual stop time is not known, which can only happen 228 * if the recording has not yet been stopped. 229 * 230 * @return the stop time of recording, or {@code null} if recording hasn't 231 * been stopped. 232 * 233 * @see Recording#getStopTime() 234 */ 235 public long getStopTime() { 236 return stopTime; 237 } 238 239 /** 240 * Returns the settings for the recording associated with this 241 * {@code RecordingInfo}. 242 * 243 * @return the recording settings, not {@code null} 244 * 245 * @see Recording#getSettings() 246 */ 247 public Map<String, String> getSettings() { 248 return settings; 249 } 250 251 /** 252 * Returns destination path where data, for the recording associated with 253 * this {@link RecordingInfo}, should be written when the recording stops, 254 * or {@code null} if the recording should not be written. 255 * 256 * @return the destination, or {@code null} if not set 257 * 258 * @see Recording#getDestination() 259 */ 260 public String getDestination() { 261 return destination; 262 } 263 264 /** 265 * Returns a string description of the recording associated with this 266 * {@code RecordingInfo} 267 * 268 * @return description, not {@code null} 269 */ 270 @Override 271 public String toString() { 272 Stringifier s = new Stringifier(); 273 s.add("name", name); 274 s.add("id", id); 275 s.add("maxAge", maxAge); 276 s.add("maxSize", maxSize); 277 return s.toString(); 278 } 279 280 /** 281 * Returns the amount data recorded by recording. associated with this 282 * {@link RecordingInfo}. 283 * 284 * @return the amount of recorded data, measured in bytes 285 */ 286 public long getSize() { 287 return size; 288 } 289 290 /** 291 * Returns {@code true} if the recording associated with this 292 * {@code RecordingInfo} should be flushed to disk, when memory buffers are 293 * full, {@code false} otherwise. 294 * 295 * @return {@code true} if recording is to disk, {@code false} otherwise 296 */ 297 public boolean isToDisk() { 298 return toDisk; 299 } 300 301 /** 302 * Returns the desired duration, measured in seconds, of the recording 303 * associated with this {@link RecordingInfo}, or {code 0} if no duration 304 * has been set. 305 * 306 * @return the desired duration, or {code 0} if no duration has been set 307 * 308 * @see Recording#getDuration() 309 */ 310 public long getDuration() { 311 return durationInSeconds; 312 } 313 314 /** 315 * Returns a {@code RecordingInfo} represented by the specified 316 * {@code CompositeData} object. 317 * <p> 318 * The specified {@code CompositeData} must have the following item names and 319 * item types to be valid. <blockquote> 320 * <table class="striped"> 321 * <caption>Supported names and types in a specified {@code CompositeData} object</caption> 322 * <thead> 323 * <tr> 324 * <th scope="col" style="text-align:left">Name</th> 325 * <th scope="col" style="text-align:left">Type</th> 326 * </tr> 327 * </thead> 328 * <tbody> 329 * <tr> 330 * <th scope="row">id</th> 331 * <td>{@code Long}</td> 332 * </tr> 333 * <tr> 334 * <th scope="row">name</th> 335 * <td>{@code String}</td> 336 * </tr> 337 * <tr> 338 * <th scope="row">state</th> 339 * <td>{@code String}</td> 340 * </tr> 341 * <tr> 342 * <th scope="row">dumpOnExit</th> 343 * <td>{@code Boolean}</td> 344 * </tr> 345 * <tr> 346 * <th scope="row">size</th> 347 * <td>{@code Long}</td> 348 * </tr> 349 * <tr> 350 * <th scope="row">toDisk</th> 351 * <td>{@code Boolean}</td> 352 * </tr> 353 * <tr> 354 * <th scope="row">maxAge</th> 355 * <td>{@code Long}</td> 356 * </tr> 357 * <tr> 358 * <th scope="row">maxSize</th> 359 * <td>{@code Long}</td> 360 * </tr> 361 * <tr> 362 * <th scope="row">startTime</th> 363 * <td>{@code Long}</td> 364 * </tr> 365 * <tr> 366 * <th scope="row">stopTime</th> 367 * <td>{@code Long}</td> 368 * </tr> 369 * <tr> 370 * <th scope="row">destination</th> 371 * <td>{@code String}</td> 372 * </tr> 373 * <tr> 374 * <th scope="row">duration</th> 375 * <td>{@code Long}</td> 376 * </tr> 377 * <tr> 378 * <th scope="row">settings</th> 379 * <td>{@code javax.management.openmbean.CompositeData[]} whose element type 380 * is the mapped type for {@link SettingDescriptorInfo} as specified in the 381 * {@link SettingDescriptorInfo#from} method.</td> 382 * </tr> 383 * </tbody> 384 * </table> 385 * </blockquote> 386 * 387 * @param cd {@code CompositeData} representing the {@code RecordingInfo} to 388 * return 389 * 390 * @throws IllegalArgumentException if {@code cd} does not represent a valid 391 * {@code RecordingInfo} 392 * 393 * @return the {@code RecordingInfo} represented by {@code cd}, or 394 * {@code null} if {@code cd} is {@code null} 395 */ 396 public static RecordingInfo from(CompositeData cd) { 397 if (cd == null) { 398 return null; 399 } 400 return new RecordingInfo(cd); 401 } 402 }