1 /* 2 * Copyright (c) 2016, 2018, 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 disk; 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 disk = 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 = (int) 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 disk = (boolean) cd.get("disk"); 96 maxAge = (Long) cd.get("maxAge"); 97 maxSize = (Long) cd.get("maxSize"); 98 startTime = (Long) cd.get("startTime"); 99 stopTime = (Long) cd.get("stopTime"); 100 destination = (String) cd.get("destination"); 101 durationInSeconds = (long) cd.get("duration"); 102 settings = new LinkedHashMap<>(); 103 Object map = cd.get("settings"); 104 if (map instanceof TabularData) { 105 TabularData td = (TabularData) map; 106 List<String> keyNames = td.getTabularType().getIndexNames(); 107 int size = keyNames.size(); 108 for (Object keys : td.keySet()) { 109 Object[] keyValues = ((List<?>) keys).toArray(); 110 for (int i = 0; i < size; i++) { 111 String key = keyNames.get(i); 112 Object value = keyValues[i]; 113 if (value instanceof String) { 114 settings.put(key, (String) value); 115 } 116 } 117 } 118 } 119 } 120 121 /** 122 * Returns the name of the recording associated with this 123 * {@code RecordingInfo}. 124 * 125 * @return the recording name, not {@code null} 126 * 127 * @see Recording#getName() 128 */ 129 public String getName() { 130 return name; 131 } 132 133 /** 134 * Returns the unique ID for the recording associated with this 135 * {@code RecordingInfo}. 136 * 137 * @return the recording ID 138 * 139 * @see Recording#getId() 140 */ 141 public long getId() { 142 return id; 143 } 144 145 /** 146 * Returns if the recording associated with this {@code RecordingInfo} 147 * should be dumped to file when the JVM exits. 148 * 149 * @return {@code true} if recording should be dumped on exit, {@code false} 150 * otherwise 151 * 152 * @see Recording#getDumpOnExit() 153 */ 154 public boolean getDumpOnExit() { 155 return dumpOnExit; 156 } 157 158 /** 159 * Returns how many seconds data should be kept on disk, or {@code 0} if 160 * data is to be kept forever. 161 * <p> 162 * In-memory recordings are not affected by maximum age. 163 * 164 * @see Recording#getMaxAge() 165 * @see Recording#setToDisk(boolean) 166 * @return how long data should be kept on disk, measured in seconds 167 * 168 */ 169 public long getMaxAge() { 170 return maxAge; 171 } 172 173 /** 174 * Returns the amount of data, measured in bytes, the recording associated 175 * with this {@code RecordingInfo}, should be kept on disk, before it's 176 * rotated away, or {@code 0} if data is to be kept indefinitely. 177 * <p> 178 * In-memory recordings are not affected by maximum size. 179 * 180 * @return the amount of data should be kept on disk, in bytes 181 * 182 * @see Recording#setToDisk(boolean) 183 * @see Recording#getMaxSize() 184 */ 185 public long getMaxSize() { 186 return maxSize; 187 } 188 189 /** 190 * Returns a {@code String} representation of state of the recording 191 * associated with this {@code RecordingInfo}. 192 * <p> 193 * Valid return values are {@code "NEW"}, {@code "DELAYED"}, {@code "STARTING"}, 194 * {@code "RUNNING"}, {@code "STOPPING"}, {@code "STOPPED"} and {@code "CLOSED"}. 195 * 196 * @return the recording state, not {@code null} 197 * 198 * @see RecordingState#toString() 199 * @see Recording#getState() 200 */ 201 public String getState() { 202 return state; 203 } 204 205 /** 206 * Returns start time of the recording associated with this 207 * {@code RecordingInfo}, measured as ms since epoch, or {@code null} if the 208 * recording hasn't started. 209 * 210 * @return the start time of the recording, or {@code null} if the recording 211 * hasn't started 212 * 213 * @see Recording#getStartTime() 214 */ 215 public long getStartTime() { 216 return startTime; 217 } 218 219 /** 220 * Returns the actual or expected stop time of the recording associated with 221 * this {@code RecordingInfo}, measured as ms since epoch, or {@code null} 222 * if the expected or actual stop time is not known, which can only happen 223 * if the recording has not yet been stopped. 224 * 225 * @return the stop time of recording, or {@code null} if recording hasn't 226 * been stopped. 227 * 228 * @see Recording#getStopTime() 229 */ 230 public long getStopTime() { 231 return stopTime; 232 } 233 234 /** 235 * Returns the settings for the recording associated with this 236 * {@code RecordingInfo}. 237 * 238 * @return the recording settings, not {@code null} 239 * 240 * @see Recording#getSettings() 241 */ 242 public Map<String, String> getSettings() { 243 return settings; 244 } 245 246 /** 247 * Returns destination path where data, for the recording associated with 248 * this {@link RecordingInfo}, should be written when the recording stops, 249 * or {@code null} if the recording should not be written. 250 * 251 * @return the destination, or {@code null} if not set 252 * 253 * @see Recording#getDestination() 254 */ 255 public String getDestination() { 256 return destination; 257 } 258 259 /** 260 * Returns a string description of the recording associated with this 261 * {@code RecordingInfo} 262 * 263 * @return description, not {@code null} 264 */ 265 @Override 266 public String toString() { 267 Stringifier s = new Stringifier(); 268 s.add("name", name); 269 s.add("id", id); 270 s.add("maxAge", maxAge); 271 s.add("maxSize", maxSize); 272 return s.toString(); 273 } 274 275 /** 276 * Returns the amount data recorded by recording. associated with this 277 * {@link RecordingInfo}. 278 * 279 * @return the amount of recorded data, measured in bytes 280 */ 281 public long getSize() { 282 return size; 283 } 284 285 /** 286 * Returns {@code true} if the recording associated with this 287 * {@code RecordingInfo} should be flushed to disk, when memory buffers are 288 * full, {@code false} otherwise. 289 * 290 * @return {@code true} if recording is to disk, {@code false} otherwise 291 */ 292 public boolean isToDisk() { 293 return disk; 294 } 295 296 /** 297 * Returns the desired duration, measured in seconds, of the recording 298 * associated with this {@link RecordingInfo}, or {code 0} if no duration 299 * has been set. 300 * 301 * @return the desired duration, or {code 0} if no duration has been set 302 * 303 * @see Recording#getDuration() 304 */ 305 public long getDuration() { 306 return durationInSeconds; 307 } 308 309 /** 310 * Returns a {@code RecordingInfo} represented by the specified 311 * {@code CompositeData} object. 312 * <p> 313 * The specified {@code CompositeData} must have the following item names and 314 * item types to be valid. <blockquote> 315 * <table class="striped"> 316 * <caption>Supported names and types in a specified {@code CompositeData} object</caption> 317 * <thead> 318 * <tr> 319 * <th scope="col" style="text-align:left">Name</th> 320 * <th scope="col" style="text-align:left">Type</th> 321 * </tr> 322 * </thead> 323 * <tbody> 324 * <tr> 325 * <th scope="row">id</th> 326 * <td>{@code Long}</td> 327 * </tr> 328 * <tr> 329 * <th scope="row">name</th> 330 * <td>{@code String}</td> 331 * </tr> 332 * <tr> 333 * <th scope="row">state</th> 334 * <td>{@code String}</td> 335 * </tr> 336 * <tr> 337 * <th scope="row">dumpOnExit</th> 338 * <td>{@code Boolean}</td> 339 * </tr> 340 * <tr> 341 * <th scope="row">size</th> 342 * <td>{@code Long}</td> 343 * </tr> 344 * <tr> 345 * <th scope="row">disk</th> 346 * <td>{@code Boolean}</td> 347 * </tr> 348 * <tr> 349 * <th scope="row">maxAge</th> 350 * <td>{@code Long}</td> 351 * </tr> 352 * <tr> 353 * <th scope="row">maxSize</th> 354 * <td>{@code Long}</td> 355 * </tr> 356 * <tr> 357 * <th scope="row">startTime</th> 358 * <td>{@code Long}</td> 359 * </tr> 360 * <tr> 361 * <th scope="row">stopTime</th> 362 * <td>{@code Long}</td> 363 * </tr> 364 * <tr> 365 * <th scope="row">destination</th> 366 * <td>{@code String}</td> 367 * </tr> 368 * <tr> 369 * <th scope="row">duration</th> 370 * <td>{@code Long}</td> 371 * </tr> 372 * <tr> 373 * <th scope="row">settings</th> 374 * <td>{@code javax.management.openmbean.CompositeData[]} whose element type 375 * is the mapped type for {@link SettingDescriptorInfo} as specified in the 376 * {@link SettingDescriptorInfo#from} method.</td> 377 * </tr> 378 * </tbody> 379 * </table> 380 * </blockquote> 381 * 382 * @param cd {@code CompositeData} representing the {@code RecordingInfo} to 383 * return 384 * 385 * @throws IllegalArgumentException if {@code cd} does not represent a valid 386 * {@code RecordingInfo} 387 * 388 * @return the {@code RecordingInfo} represented by {@code cd}, or 389 * {@code null} if {@code cd} is {@code null} 390 */ 391 public static RecordingInfo from(CompositeData cd) { 392 if (cd == null) { 393 return null; 394 } 395 return new RecordingInfo(cd); 396 } 397 }