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