feat(jdk8): move files to new folder to avoid resources compiled.

This commit is contained in:
2025-09-07 15:25:52 +08:00
parent 3f0047bf6f
commit 8c35cfb1c0
17415 changed files with 217 additions and 213 deletions

View File

@@ -0,0 +1,241 @@
/*
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.management.jfr;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.TabularData;
import jdk.jfr.Configuration;
/**
* Management representation of a {@code Configuration}.
*
* @see Configuration
*
* @since 8
*/
public final class ConfigurationInfo {
private final Map<String, String> settings;
private final String name;
private final String label;
private final String description;
private final String provider;
private final String contents;
ConfigurationInfo(Configuration config) {
this.settings = config.getSettings();
this.name = config.getName();
this.label = config.getLabel();
this.description = config.getDescription();
this.provider = config.getProvider();
this.contents = config.getContents();
}
private ConfigurationInfo(CompositeData cd) {
this.settings = createMap(cd.get("settings"));
this.name = (String) cd.get("name");
this.label = (String) cd.get("label");
this.description = (String) cd.get("description");
this.provider = (String) cd.get("provider");
this.contents = (String) cd.get("contents");
}
private static Map<String, String> createMap(Object o) {
if (o instanceof TabularData) {
TabularData td = (TabularData) o;
Collection<?> values = td.values();
Map<String, String> map = new HashMap<>(values.size());
for (Object value : td.values()) {
if (value instanceof CompositeData) {
CompositeData cdRow = (CompositeData) value;
Object k = cdRow.get("key");
Object v = cdRow.get("value");
if (k instanceof String && v instanceof String) {
map.put((String) k, (String) v);
}
}
}
return Collections.unmodifiableMap(map);
}
return Collections.emptyMap();
}
/**
* Returns the provider of the configuration associated with this
* {@code ConfigurationInfo} (for example, {@code "OpenJDK"}).
*
* @return the provider, or {@code null} if doesn't exist
*
* @see Configuration#getProvider()
*/
public String getProvider() {
return provider;
}
/**
* Returns the textual representation of the configuration associated with
* this {@code ConfigurationInfo}, typically the contents of the
* configuration file that was used to create the configuration.
*
* @return contents, or {@code null} if doesn't exist
*
* @see Configuration#getContents()
*/
public String getContents() {
return contents;
}
/**
* Returns the settings for the configuration associated with this
* {@code ConfigurationInfo}.
*
* @return a {@code Map} with settings, not {@code null}
*
* @see Configuration#getSettings()
*/
public Map<String, String> getSettings() {
return settings;
}
/**
* Returns the human-readable name (for example, {@code "Continuous"} or {@code "Profiling"}) for
* the configuration associated with this {@code ConfigurationInfo}
*
* @return the label, or {@code null} if doesn't exist
*
* @see Configuration#getLabel()
*/
public String getLabel() {
return label;
}
/**
* Returns the name of the configuration associated with this
* {@code ConfigurationInfo} (for example, {@code "default"}).
*
* @return the name, or {@code null} if doesn't exist
*
* @see Configuration#getLabel()
*/
public String getName() {
return name;
}
/**
* Returns a short sentence that describes the configuration associated with
* this {@code ConfigurationInfo} (for example, {@code "Low
* overhead configuration safe for continuous use in production
* environments"}.
*
* @return the description, or {@code null} if doesn't exist
*/
public String getDescription() {
return description;
}
/**
* Returns a {@code ConfigurationInfo} object represented by the specified
* {@code CompositeData}.
* <p>
* The following table shows the required attributes that the specified {@code CompositeData} must contain.
* <blockquote>
* <table class="striped">
* <caption>Required names and types for CompositeData</caption>
* <thead>
* <tr>
* <th scope="col" style="text-align:left">Name</th>
* <th scope="col" style="text-align:left">Type</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <th scope="row">name</th>
* <td>{@code String}</td>
* </tr>
* <tr>
* <th scope="row">label</th>
* <td>{@code String}</td>
* </tr>
* <tr>
* <th scope="row">description</th>
* <td>{@code String}</td>
* </tr>
* <tr>
* <th scope="row">provider</th>
* <td>{@code String}</td>
* </tr>
* <tr>
* <th scope="row">contents</th>
* <td>{@code String}</td>
* </tr>
*
* <tr>
* <th scope="row">settings</th>
* <td>{@code javax.management.openmbean.TabularData} with a
* {@code TabularType} with the keys {@code "key"} and {@code "value"}, both
* of the {@code String} type</td>
* </tr>
* </tbody>
* </table>
* </blockquote>
*
* @param cd {@code CompositeData} representing a {@code ConfigurationInfo}
*
* @throws IllegalArgumentException if {@code cd} does not represent a
* {@code ConfigurationInfo} with the required attributes
*
* @return a {@code ConfigurationInfo} object represented by {@code cd} if
* {@code cd} is not {@code null}, {@code null} otherwise
*/
public static ConfigurationInfo from(CompositeData cd) {
if (cd == null) {
return null;
}
return new ConfigurationInfo(cd);
}
/**
* Returns a description of the configuration that is associated with this
* {@code ConfigurationInfo}.
*
* @return the description of the configuration, not {@code null}
*/
@Override
public String toString() {
Stringifier s = new Stringifier();
s.add("name", name);
s.add("label", label);
s.add("description", description);
s.add("provider", provider);
return s.toString();
}
}

View File

@@ -0,0 +1,262 @@
/*
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.management.jfr;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.StringJoiner;
import javax.management.openmbean.CompositeData;
import jdk.jfr.Category;
import jdk.jfr.EventType;
import jdk.jfr.SettingDescriptor;
/**
* Management representation of an {@code EventType}.
*
* @see EventType
*
* @since 8
*/
public final class EventTypeInfo {
private final List<SettingDescriptorInfo> settings;
private final long id;
private final String name;
private final String description;
private final String label;
private final List<String> categoryNames;
// package private
EventTypeInfo(EventType eventType) {
this.settings = creatingSettingDescriptorInfos(eventType);
this.id = eventType.getId();
this.name = eventType.getName();
this.label = eventType.getLabel();
this.description = eventType.getDescription();
this.categoryNames = eventType.getCategoryNames();
}
private EventTypeInfo(CompositeData cd) {
this.settings = createSettings(cd.get("settings"));
this.id = (long) cd.get("id");
this.name = (String) cd.get("name");
this.label = (String) cd.get("label");
this.description = (String) cd.get("description");
this.categoryNames = createCategoryNames((Object[]) cd.get("category"));
}
private static List<String> createCategoryNames(Object[] array) {
List<String> list = new ArrayList<>(array.length);
for (int i = 0; i < array.length; i++) {
list.add((String) array[i]);
}
return Collections.unmodifiableList(list);
}
private static List<SettingDescriptorInfo> creatingSettingDescriptorInfos(EventType eventType) {
List<SettingDescriptor> settings = eventType.getSettingDescriptors();
List<SettingDescriptorInfo> settingDescriptorInfos = new ArrayList<>(settings.size());
for (SettingDescriptor s : settings) {
settingDescriptorInfos.add(new SettingDescriptorInfo(s));
}
return Collections.unmodifiableList(settingDescriptorInfos);
}
private static List<SettingDescriptorInfo> createSettings(Object settings) {
if (settings != null && settings.getClass().isArray()) {
Object[] settingsArray = (Object[]) settings;
List<SettingDescriptorInfo> list = new ArrayList<>(settingsArray.length);
for (Object cd : settingsArray) {
if (cd instanceof CompositeData) {
list.add(SettingDescriptorInfo.from((CompositeData) cd));
}
}
return Collections.unmodifiableList(list);
}
return Collections.emptyList();
}
/**
* Returns the label, a human-readable name, associated with the event type
* for this {@code EventTypeInfo} (for example, {@code "Garbage Collection"}).
*
* @return the label, or {@code null} if a label is not set
*
* @see EventType#getLabel()
*/
public String getLabel() {
return label;
}
/**
*
* Returns the list of human-readable names that makes up the category for this
* {@code EventTypeInfo} (for example, {@code "Java Virtual Machine"} or {@code "Garbage Collector"}).
*
* @return an immutable list of category names, or a list with the name
* {@code "Uncategorized"} if no category has been set
*
* @see EventType#getCategoryNames()
* @see Category
*/
public List<String> getCategoryNames() {
return categoryNames;
}
/**
* Returns the unique ID for the event type associated with this
* {@code EventTypeInfo}, not guaranteed to be the same for different Java
* Virtual Machines (JVMs) instances.
*
* @return the ID
*
* @see EventType#getId()
*/
public long getId() {
return id;
}
/**
* Returns the name for the event type associated with this
* {@code EventTypeInfo} (for example, {@code "jdk.GarbageCollection"}).
*
* @return the name, not {@code null}
*
* @see EventType#getName()
*/
public String getName() {
return name;
}
/**
* Returns a short sentence or two describing the event type associated with
* this {@code EventTypeInfo}, for example
* {@code "Garbage collection performed by the JVM""}.
*
* @return the description, or {@code null} if no description exists
*
* @see EventType#getDescription()
*/
public String getDescription() {
return description;
}
/**
* Returns settings for the event type associated with this
* {@code EventTypeInfo}.
*
* @return the settings, not {@code null}
*
* @see EventType#getSettingDescriptors()
*/
public List<SettingDescriptorInfo> getSettingDescriptors() {
return settings;
}
/**
* Returns a description of this {@code EventTypeInfo}.
*
* @return description, not {@code null}
*/
@Override
public String toString() {
Stringifier s = new Stringifier();
s.add("id", id);
s.add("name", name);
s.add("label", label);
s.add("description", description);
StringJoiner sj = new StringJoiner(", ", "{", "}");
for (String categoryName : categoryNames) {
sj.add(categoryName);
}
s.add("category", sj.toString());
return s.toString();
}
/**
* Returns an {@code EventType} represented by the specified
* {@code CompositeData}
* <p>
* The supplied {@code CompositeData} must have the following item names and
* item types to be valid. <blockquote>
* <table class="striped">
* <caption>The name and type the specified CompositeData must contain</caption>
* <thead>
* <tr>
* <th scope="col" style="text-align:left">Name</th>
* <th scope="col" style="text-align:left">Type</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <th scope="row">id</th>
* <td>{@code Long}</td>
* </tr>
* <tr>
* <th scope="row">name</th>
* <td>{@code String}</td>
* </tr>
* <tr>
* <th scope="row">label</th>
* <td>{@code String}</td>
* </tr>
* <tr>
* <th scope="row">description</th>
* <td>{@code String}</td>
* </tr>
* <tr>
* <th scope="row">category</th>
* <td><code>ArrayType(1, SimpleType.STRING)</code></td>
* </tr>
* <tr>
* <th scope="row">settings</th>
* <td>{@code javax.management.openmbean.CompositeData[]} whose element type
* is the mapped type for {@link SettingDescriptorInfo} as specified in the
* {@link SettingDescriptorInfo#from} method.</td>
*
* </tr>
* </tbody>
* </table>
* </blockquote>
*
* @param cd {@code CompositeData} representing the {@code EventTypeInfo} to
* return
*
* @throws IllegalArgumentException if {@code cd} does not represent a valid
* {@code EventTypeInfo}
*
* @return an {@code EventTypeInfo}, or {@code null} if {@code cd} is
* {@code null}
*/
public static EventTypeInfo from(CompositeData cd) {
if (cd == null) {
return null;
}
return new EventTypeInfo(cd);
}
}

View File

@@ -0,0 +1,639 @@
/*
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.management.jfr;
import java.io.IOException;
import java.lang.management.PlatformManagedObject;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import jdk.jfr.Configuration;
import jdk.jfr.EventType;
import jdk.jfr.Recording;
/**
* Management interface for controlling Flight Recorder.
* <p>
* The object name for identifying the MXBean in the platform MBean
* server is: <blockquote> {@code jdk.management.jfr:type=FlightRecorder} </blockquote>
* <p>
* Flight Recorder can be configured in the following ways:
* <ul>
* <li><b>Recording options</b><br>
* Specify how long a recording should last, and where and when data
* should be dumped.</li>
* <li><b>Settings</b><br>
* Specify which events should be enabled and what kind information each
* event should capture.</li>
* <li><b>Configurations</b><br>
* Predefined sets of settings, typically derived from a settings file,
* that specify the configuration of multiple events simultaneously.</li>
* </ul>
* <p>
* See the package {@code jdk.jfr} documentation for descriptions of the settings
* syntax and the {@link ConfigurationInfo} class documentation for configuration information.
*
* <h3>Recording options</h3>
* <p>
* The following table shows the options names to use with {@link #setRecordingOptions(long, Map)}
* and {@link #getRecordingOptions(long)}.
*
* <table class="striped">
* <caption>Recording options</caption>
* <thead>
* <tr>
* <th scope="col">Name</th>
* <th scope="col">Descripion</th>
* <th scope="col">Default value</th>
* <th scope="col">Format</th>
* <th scope="col">Example values</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <th scope="row">{@code name}</th>
* <td>Sets a human-readable recording name</td>
* <td>String representation of the recording id</td>
* <td>{@code String}</td>
* <td>{@code "My Recording"}, <br>
* {@code "profiling"}</td>
* </tr>
* <tr>
* <th scope="row">{@code maxAge}</th>
* <td>Specify the length of time that the data is kept in the disk repository until the
* oldest data may be deleted. Only works if {@code disk=true}, otherwise
* this parameter is ignored.</td>
* <td>{@code "0"} (no limit)</td>
* <td>{@code "0"} if no limit is imposed, otherwise a string
* representation of a positive {@code Long} value followed by an empty space
* and one of the following units,<br>
* <br>
* {@code "ns"} (nanoseconds)<br>
* {@code "us"} (microseconds)<br>
* {@code "ms"} (milliseconds)<br>
* {@code "s"} (seconds)<br>
* {@code "m"} (minutes)<br>
* {@code "h"} (hours)<br>
* {@code "d"} (days)<br>
* </td>
* <td>{@code "2 h"},<br>
* {@code "24 h"},<br>
* {@code "2 d"},<br>
* {@code "0"}</td>
* </tr>
* <tr>
* <th scope="row">{@code maxSize}</th>
* <td>Specifies the size, measured in bytes, at which data is kept in disk
* repository. Only works if
* {@code disk=true}, otherwise this parameter is ignored.</td>
* <td>{@code "0"} (no limit)</td>
* <td>String representation of a {@code Long} value, must be positive</td>
* <td>{@code "0"}, <br>
* {@code "1000000000"}</td>
* </tr>
* <tr>
* <th scope="row">{@code dumpOnExit}</th>
* <td>Dumps recording data to disk on Java Virtual Machine (JVM) exit</td>
* <td>{@code "false"}</td>
* <td>String representation of a {@code Boolean} value, {@code "true"} or
* {@code "false"}</td>
* <td>{@code "true"},<br>
* {@code "false"}</td>
* </tr>
* <tr>
* <th scope="row">{@code destination}</th>
* <td>Specifies the path where recording data is written when the recording stops.</td>
* <td>{@code "false"}</td>
* <td>See {@code Paths#getPath} for format. <br>
* If this method is invoked from another process, the data is written on the
* machine where the target JVM is running. If destination is a relative path, it
* is relative to the working directory where the target JVM was started.}</td>
* <td>{@code "c:\recording\recotding.jfr"},<br>
* {@code "/recordings/recording.jfr"}, {@code "recording.jfr"}</td>
* </tr>
* <tr>
* <th scope="row">{@code disk}</th>
* <td>Stores recorded data as it is recorded</td>
* <td><code>"false"</code></td>
* <td>String representation of a {@code Boolean} value, {@code "true"} or
* {@code "false"}</td>
* <td>{@code "true"},<br>
* {@code "false"}</td>
* <tr>
* <th scope="row">{@code duration}</th>
* <td>Sets how long the recording should be running</td>
* <td>{@code "0"} (no limit, continuous)</td>
* <td>{@code "0"} if no limit should be imposed, otherwise a string
* representation of a positive {@code Long} followed by an empty space and one
* of the following units:<br>
* <br>
* {@code "ns"} (nanoseconds)<br>
* {@code "us"} (microseconds)<br>
* {@code "ms"} (milliseconds)<br>
* {@code "s"} (seconds)<br>
* {@code "m"} (minutes)<br>
* {@code "h"} (hours)<br>
* {@code "d"} (days)<br>
* </td>
* <td>{@code "60 s"},<br>
* {@code "10 m"},<br>
* {@code "4 h"},<br>
* {@code "0"}</td>
* </tr>
* </tbody>
* </table>
*
* @since 8
*/
public interface FlightRecorderMXBean extends PlatformManagedObject {
/**
* String representation of the {@code ObjectName} for the
* {@code FlightRecorderMXBean}.
*/
public static final String MXBEAN_NAME = "jdk.management.jfr:type=FlightRecorder";
/**
* Creates a recording, but doesn't start it.
*
* @return a unique ID that can be used to start, stop, close and
* configure the recording
*
* @throws IllegalStateException if Flight Recorder can't be created (for
* example, if the Java Virtual Machine (JVM) lacks Flight Recorder
* support, or if the file repository can't be created or accessed)
*
* @throws java.lang.SecurityException if a security manager exists and the
* caller does not have {@code ManagementPermission("control")}
*
* @see Recording
*/
long newRecording() throws IllegalStateException, SecurityException;
/**
* Creates a snapshot recording of all available recorded data.
* <p>
* A snapshot is a synthesized recording in a stopped state. If no data is
* available, a recording with size {@code 0} is returned.
* <p>
* A snapshot provides stable access to data for later operations (for example,
* operations to change the time interval or to reduce the data size).
* <p>
* The caller must close the recording when access to the data is no longer
* needed.
*
* @return a snapshot of all available recording data, not {@code null}
*
* @throws java.lang.SecurityException if a security manager exists and the
* caller does not have {@code ManagementPermission("control")}
*
* @return a unique ID that can be used for reading recording data.
*
* @see Recording
*/
public long takeSnapshot();
/**
* Creates a copy of an existing recording, useful for extracting parts of a
* recording.
* <p>
* The cloned recording contains the same recording data as the
* original, but it has a new ID and a name prefixed with
* {@code "Clone of recording"}. If the original recording is running, then
* the clone is also running.
*
* @param recordingId the recording ID of the recording to create a clone
* from
*
* @param stop if the newly created clone is stopped before
* returning.
*
* @return a unique ID that can be used to start, stop,
* close and configure the recording
*
* @throws IllegalArgumentException if a recording with the specified ID
* doesn't exist
*
* @throws java.lang.SecurityException if a security manager exists and the
* caller does not have {@code ManagementPermission("control")}
*
* @see Recording
*/
long cloneRecording(long recordingId, boolean stop) throws IllegalArgumentException, SecurityException;
/**
* Starts the recording with the specified ID.
* <p>
* A recording that is stopped can't be restarted.
*
* @param recordingId ID of the recording to start
*
* @throws IllegalArgumentException if a recording with the specified ID
* doesn't exist
*
* @throws java.lang.SecurityException if a security manager exists and the
* caller does not have {@code ManagementPermission("control")}
*
* @see Recording
*/
void startRecording(long recordingId) throws IllegalStateException, SecurityException;
/**
* Stops the running recording with the specified ID.
*
* @param recordingId the ID of the recording to stop
*
* @return {@code true} if the recording is stopped, {@code false}
* otherwise
*
* @throws IllegalArgumentException if a recording with the specified ID
* doesn't exist
* @throws IllegalStateException if the recording is not running
* @throws java.lang.SecurityException if a security manager exists and the
* caller does not have {@code ManagementPermission("control")}
*
* @see #newRecording()
*/
boolean stopRecording(long recordingId) throws IllegalArgumentException, IllegalStateException, SecurityException;
/**
* Closes the recording with the specified ID and releases any system
* resources that are associated with the recording.
* <p>
* If the recording is already closed, invoking this method has no effect.
*
* @param recordingId the ID of the recording to close
*
* @throws IllegalArgumentException if a recording with the specified ID
* doesn't exist
* @throws IOException if an I/O error occurs
* @throws java.lang.SecurityException if a security manager exists and the
* caller does not have {@code ManagementPermission("control")}
*
* @see #newRecording()
*/
void closeRecording(long recordingId) throws IOException;
/**
* Opens a data stream for the recording with the specified ID, or {@code 0}
* to get data irrespective of recording.
* <table class="striped">
* <caption>Recording stream options</caption>
* <thead>
* <tr>
* <th scope="col">Name</th>
* <th scope="col">Descripion</th>
* <th scope="col">Default value</th>
* <th scope="col">Format</th>
* <th scope="col">Example values</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <th scope="row">{@code startTime}</th>
* <td>Specifies the point in time to start a recording stream. Due to
* how data is stored, some events that start or end prior to the
* start time may be included.</td>
* <td>{@code Instant.MIN_VALUE.toString()}</td>
* <td>ISO-8601. See {@link Instant#toString}<br>
* or milliseconds since epoch</td>
* <td>{@code "2015-11-03T00:00"},<br>
* {@code "1446508800000"}</td>
* </tr>
* <tr>
* <th scope="row">{@code endTime}</th>
* <td>Specifies the point in time to end a recording stream. Due to how
* data is stored, some events that start or end after the end time may
* be included.</td>
* <td>{@code Instant.MAX_VALUE.toString()}</td>
* <td>ISO-8601. See {@link Instant#toString} <br>
* or milliseconds since epoch</td>
* <td>{@code "2015-11-03T01:00"}, <br>
* {@code "1446512400000"}</td>
* </tr>
*
* <tr>
* <th scope="row">{@code blockSize}</th>
* <td>Specifies the maximum number of bytes to read with a call to {@code readStream}
* </td>
* <td>{@code "50000"}</td>
* <td>A positive {@code long} value. <br>
* <br>
* Setting {@code blockSize} to a very high value may result in
* {@link OutOfMemoryError} or an {@link IllegalArgumentException}, if the
* Java Virtual Machine (JVM) deems the value too large to handle.</td>
* <td>{@code "50000"},<br>
* {@code "1000000"},<br>
* </tr>
* </tbody>
* </table>
* If an option is omitted from the map the default value is used.
* <p>
* The recording with the specified ID must be stopped before a stream can
* be opened. This restriction might be lifted in future releases.
*
* @param recordingId ID of the recording to open the stream for
*
* @param streamOptions a map that contains the options that controls the amount of data
* and how it is read, or {@code null} to get all data for the
* recording with the default block size
*
* @return a unique ID for the stream.
*
* @throws IllegalArgumentException if a recording with the iD doesn't
* exist, or if {@code options} contains invalid values
*
* @throws IOException if the recording is closed, an I/O error occurs, or
* no data is available for the specified recording or
* interval
*
* @throws java.lang.SecurityException if a security manager exists and the
* caller does not have {@code ManagementPermission("control")}
*/
long openStream(long recordingId, Map<String, String> streamOptions) throws IOException;
/**
* Closes the recording stream with the specified ID and releases any system
* resources that are associated with the stream.
* <p>
* If the stream is already closed, invoking this method has no effect.
*
* @param streamId the ID of the stream
*
* @throws IllegalArgumentException if a stream with the specified ID doesn't
* exist
* @throws IOException if an I/O error occurs while trying to close the stream
* @throws java.lang.SecurityException if a security manager exists and the
* caller does not have {@code ManagementPermission("control")}
*
* @see #openStream(long, Map)
*/
void closeStream(long streamId) throws IOException;
/**
* Reads a portion of data from the stream with the specified ID, or returns
* {@code null} if no more data is available.
* <p>
* To read all data for a recording, invoke this method repeatedly until
* {@code null} is returned.
*
* @param streamId the ID of the stream
*
* @return byte array that contains recording data, or {@code null} when no more
* data is available
* @throws IOException if the stream is closed, or an I/O error occurred while
* trying to read the stream
* @throws IllegalArgumentException if no recording with the stream ID exists
* @throws java.lang.SecurityException if a security manager exists and the
* caller does not have {@code ManagementPermission("monitor")}
*/
byte[] readStream(long streamId) throws IOException;
/**
* Returns a map that contains the options for the recording with the
* specified ID (for example, the destination file or time span to keep
* recorded data).
* <p>
* See {@link FlightRecorderMXBean} for available option names.
*
* @param recordingId the ID of the recording to get options for
*
* @return a map describing the recording options, not {@code null}
*
* @throws IllegalArgumentException if no recording with the
* specified ID exists
* @throws java.lang.SecurityException if a security manager exists and the
* caller does not have {@code ManagementPermission("monitor")}
*
*/
Map<String, String> getRecordingOptions(long recordingId) throws IllegalArgumentException;
/**
* Returns a {@code Map} that contains the settings for the recording with the specified ID,
* (for example, the event thresholds)
* <p>
* If multiple recordings are running at the same time, more data could be
* recorded than what is specified in the {@code Map} object.
* <p>
* The name in the {@code Map} is the event name and the setting name separated by
* {@code "#"} (for example, {@code "jdk.VMInfo#period"}). The value
* is a textual representation of the settings value (for example,
* {@code "60 s"}).
*
* @param recordingId the ID of the recordings to get settings for
*
* @return a map that describes the recording settings, not {@code null}
*
* @throws IllegalArgumentException if no recording with the specified ID exists
* @throws java.lang.SecurityException if a security manager exists and the
* caller does not have {@code ManagementPermission("monitor")}
*/
Map<String, String> getRecordingSettings(long recordingId) throws IllegalArgumentException;
/**
* Sets a configuration as a string representation for the recording with the
* specified ID.
*
* @param recordingId ID of the recording
* @param contents a string representation of the configuration file to use,
* not {@code null}
* @throws IllegalArgumentException if no recording with the
* specified ID exists or if the configuration could not be parsed.
* @throws java.lang.SecurityException if a security manager exists and the
* caller does not have {@code ManagementPermission("control")}
*
* @see Configuration#getContents()
*/
void setConfiguration(long recordingId, String contents) throws IllegalArgumentException;
/**
* Sets a predefined configuration for the recording with the specified ID.
*
* @param recordingId ID of the recording to set the configuration for
* @param configurationName the name of the configuration (for example,
* {@code "profile"} or {@code "default"}), not {@code null}
* @throws IllegalArgumentException if no recording with the
* specified ID exists
* @throws java.lang.SecurityException if a security manager exists and the
* caller does not have {@code ManagementPermission("control")}
*
* @see #getConfigurations()
*/
void setPredefinedConfiguration(long recordingId, String configurationName) throws IllegalArgumentException;
/**
* Sets and replaces all previous settings for the specified recording.
* <p>
* A setting consists of a name/value pair, where <em>name</em> specifies the
* event and setting to configure, and the <em>value</em> specifies what to set
* it to.
* <p>
* The name can be formed in the following ways:
* <p>
* {@code
* <event-name> + "#" + <setting-name>
* }
* <p>
* or
* <p>
* {@code
* <event-id> + "#" + <setting-name>
* }
* <p>
* For example, to set the sample interval of the CPU Load event to once every
* second, use the name {@code "jdk.CPULoad#period"} and the value
* {@code "1 s"}. If multiple events use the same name, for example if an event
* class is loaded in multiple class loaders, and differentiation is needed
* between them, then the name is {@code "56#period"}. The ID for an event is
* obtained by invoking {@link jdk.jfr.EventType#getId()} method and is valid
* for the Java Virtual Machine (JVM) instance that the event is registered in.
* <p>
* A list of available event names is retrieved by invoking
* {@link jdk.jfr.FlightRecorder#getEventTypes()} and
* {@link jdk.jfr.EventType#getName()}. A list of available settings for an
* event type is obtained by invoking
* {@link jdk.jfr.EventType#getSettingDescriptors()} and
* {@link jdk.jfr.ValueDescriptor#getName()}.
*
* @param recordingId ID of the recording
*
* @param settings name value map of the settings to set, not {@code null}
*
* @throws IllegalArgumentException if no recording with the specified ID exists
* @throws java.lang.SecurityException if a security manager exists and the
* caller does not have {@code ManagementPermission("control")}
*
* @see Recording#getId()
*/
void setRecordingSettings(long recordingId, Map<String, String> settings) throws IllegalArgumentException;
/**
* Configures the recording options (for example, destination file and time span
* to keep data).
* <p>
* See {@link FlightRecorderMXBean} for a description of the options and values
* that can be used. Setting a value to {@code null} restores the value to the
* default value.
*
* @param recordingId the ID of the recording to set options for
*
* @param options name/value map of the settings to set, not {@code null}
*
* @throws IllegalArgumentException if no recording with the specified ID exists
* @throws java.lang.SecurityException if a security manager exists, and the
* caller does not have {@code ManagementPermission("control")} or an
* option contains a file that the caller does not have permission to
* operate on.
* @see Recording#getId()
*/
void setRecordingOptions(long recordingId, Map<String, String> options) throws IllegalArgumentException;
/**
* Returns the list of the available recordings, not necessarily running.
* <p>
* <b>MBeanServer access</b>:<br>
* The mapped type of {@code RecordingInfo} is {@code CompositeData} with
* attributes as specified in the {@link RecordingInfo#from
* RecordingInfo.from} method.
*
* @return list of recordings, not {@code null}
*
* @throws java.lang.SecurityException if a security manager exists and the
* caller does not have {@code ManagementPermission("monitor")}
*
* @see RecordingInfo
* @see Recording
*/
List<RecordingInfo> getRecordings();
/**
* Returns the list of predefined configurations for this Java Virtual Machine (JVM).
* <p>
* <b>MBeanServer access</b>:<br>
* The mapped type of {@code ConfigurationInfo} is {@code CompositeData}
* with attributes as specified in the {@link ConfigurationInfo#from
* ConfigurationInfo.from} method.
*
* @return the list of predefined configurations, not {@code null}
*
* @throws java.lang.SecurityException if a security manager exists and the
* caller does not have {@code ManagementPermission("monitor")}
*
* @see ConfigurationInfo
* @see Configuration
*/
List<ConfigurationInfo> getConfigurations();
/**
* Returns the list of currently registered event types.
* <p>
* <b>MBeanServer access</b>:<br>
* The mapped type of {@code EventTypeInfo} is {@code CompositeData} with
* attributes as specified in the {@link EventTypeInfo#from
* EventTypeInfo.from} method.
*
* @return the list of registered event types, not {@code null}
*
* @throws java.lang.SecurityException if a security manager exists and the
* caller does not have {@code ManagementPermission("monitor")}
*
* @see EventTypeInfo
* @see EventType
*/
List<EventTypeInfo> getEventTypes();
/**
* Writes recording data to the specified file.
* <p>
* If this method is invoked remotely from another process, the data is written
* to a file named {@code outputFile} on the machine where the target Java
* Virtual Machine (JVM) is running. If the file location is a relative path, it
* is relative to the working directory where the target JVM was started.
*
* @param recordingId the ID of the recording to dump data for
*
* @param outputFile the system-dependent file name where data is written, not
* {@code null}
*
* @throws IOException if the recording can't be dumped due to an I/O error (for
* example, an invalid path)
*
* @throws IllegalArgumentException if a recording with the specified ID doesn't
* exist
*
* @throws IllegalStateException if the recording is not yet started or if it is
* already closed
*
* @throws SecurityException if a security manager exists and its
* {@code SecurityManager.checkWrite(java.lang.String)} method denies
* write access to the named file or the caller does not have
* {@code ManagmentPermission("control")}
*
* @see java.nio.file.Path#toString()
* @see Recording#dump(java.nio.file.Path)
*/
void copyTo(long recordingId, String outputFile) throws IOException, SecurityException;
}

View File

@@ -0,0 +1,440 @@
/*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.management.jfr;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.nio.file.Paths;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.text.ParseException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import javax.management.AttributeChangeNotification;
import javax.management.AttributeNotFoundException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanNotificationInfo;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationEmitter;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.management.StandardEmitterMBean;
import jdk.jfr.Configuration;
import jdk.jfr.EventType;
import jdk.jfr.FlightRecorder;
import jdk.jfr.FlightRecorderListener;
import jdk.jfr.FlightRecorderPermission;
import jdk.jfr.Recording;
import jdk.jfr.RecordingState;
import jdk.jfr.internal.management.ManagementSupport;
// Instantiated by service provider
final class FlightRecorderMXBeanImpl extends StandardEmitterMBean implements FlightRecorderMXBean, NotificationEmitter {
final class MXBeanListener implements FlightRecorderListener {
private final NotificationListener listener;
private final NotificationFilter filter;
private final Object handback;
private final AccessControlContext context;
public MXBeanListener(NotificationListener listener, NotificationFilter filter, Object handback) {
this.context = AccessController.getContext();
this.listener = listener;
this.filter = filter;
this.handback = handback;
}
public void recordingStateChanged(Recording recording) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
sendNotification(createNotication(recording));
return null;
}
}, context);
}
}
private static final String ATTRIBUTE_RECORDINGS = "Recordings";
private static final String OPTION_MAX_SIZE = "maxSize";
private static final String OPTION_MAX_AGE = "maxAge";
private static final String OPTION_NAME = "name";
private static final String OPTION_DISK = "disk";
private static final String OPTION_DUMP_ON_EXIT = "dumpOnExit";
private static final String OPTION_DURATION = "duration";
private static final List<String> OPTIONS = Arrays.asList(new String[] { OPTION_DUMP_ON_EXIT, OPTION_DURATION, OPTION_NAME, OPTION_MAX_AGE, OPTION_MAX_SIZE, OPTION_DISK, });
private final StreamManager streamHandler = new StreamManager();
private final Map<Long, Object> changes = new ConcurrentHashMap<>();
private final AtomicLong sequenceNumber = new AtomicLong();
private final List<MXBeanListener> listeners = new CopyOnWriteArrayList<>();
private FlightRecorder recorder;
FlightRecorderMXBeanImpl() {
super(FlightRecorderMXBean.class, true, new NotificationBroadcasterSupport(createNotificationInfo()));
}
@Override
public void startRecording(long id) {
MBeanUtils.checkControl();
getExistingRecording(id).start();
}
@Override
public boolean stopRecording(long id) {
MBeanUtils.checkControl();
return getExistingRecording(id).stop();
}
@Override
public void closeRecording(long id) {
MBeanUtils.checkControl();
getExistingRecording(id).close();
}
@Override
public long openStream(long id, Map<String, String> options) throws IOException {
MBeanUtils.checkControl();
if (!FlightRecorder.isInitialized()) {
throw new IllegalArgumentException("No recording available with id " + id);
}
// Make local copy to prevent concurrent modification
Map<String, String> s = options == null ? new HashMap<>() : new HashMap<>(options);
Instant starttime = MBeanUtils.parseTimestamp(s.get("startTime"), Instant.MIN);
Instant endtime = MBeanUtils.parseTimestamp(s.get("endTime"), Instant.MAX);
int blockSize = MBeanUtils.parseBlockSize(s.get("blockSize"), StreamManager.DEFAULT_BLOCK_SIZE);
InputStream is = getExistingRecording(id).getStream(starttime, endtime);
if (is == null) {
throw new IOException("No recording data available");
}
return streamHandler.create(is, blockSize).getId();
}
@Override
public void closeStream(long streamIdentifier) throws IOException {
MBeanUtils.checkControl();
streamHandler.getStream(streamIdentifier).close();
}
@Override
public byte[] readStream(long streamIdentifier) throws IOException {
MBeanUtils.checkMonitor();
return streamHandler.getStream(streamIdentifier).read();
}
@Override
public List<RecordingInfo> getRecordings() {
MBeanUtils.checkMonitor();
if (!FlightRecorder.isInitialized()) {
return Collections.emptyList();
}
return MBeanUtils.transformList(getRecorder().getRecordings(), RecordingInfo::new);
}
@Override
public List<ConfigurationInfo> getConfigurations() {
MBeanUtils.checkMonitor();
return MBeanUtils.transformList(Configuration.getConfigurations(), ConfigurationInfo::new);
}
@Override
public List<EventTypeInfo> getEventTypes() {
MBeanUtils.checkMonitor();
List<EventType> eventTypes = AccessController.doPrivileged(new PrivilegedAction<List<EventType>>() {
@Override
public List<EventType> run() {
return ManagementSupport.getEventTypes();
}
}, null, new FlightRecorderPermission("accessFlightRecorder"));
return MBeanUtils.transformList(eventTypes, EventTypeInfo::new);
}
@Override
public Map<String, String> getRecordingSettings(long recording) throws IllegalArgumentException {
MBeanUtils.checkMonitor();
return getExistingRecording(recording).getSettings();
}
@Override
public void setRecordingSettings(long recording, Map<String, String> values) throws IllegalArgumentException {
Objects.requireNonNull(values);
MBeanUtils.checkControl();
getExistingRecording(recording).setSettings(values);
}
@Override
public long newRecording() {
MBeanUtils.checkControl();
getRecorder(); // ensure notification listener is setup
return AccessController.doPrivileged(new PrivilegedAction<Recording>() {
@Override
public Recording run() {
return new Recording();
}
}, null, new FlightRecorderPermission("accessFlightRecorder")).getId();
}
@Override
public long takeSnapshot() {
MBeanUtils.checkControl();
return getRecorder().takeSnapshot().getId();
}
@Override
public void setConfiguration(long recording, String configuration) throws IllegalArgumentException {
Objects.requireNonNull(configuration);
MBeanUtils.checkControl();
try {
Configuration c = Configuration.create(new StringReader(configuration));
getExistingRecording(recording).setSettings(c.getSettings());
} catch (IOException | ParseException e) {
throw new IllegalArgumentException("Could not parse configuration", e);
}
}
@Override
public void setPredefinedConfiguration(long recording, String configurationName) throws IllegalArgumentException {
Objects.requireNonNull(configurationName);
MBeanUtils.checkControl();
Recording r = getExistingRecording(recording);
for (Configuration c : Configuration.getConfigurations()) {
if (c.getName().equals(configurationName)) {
r.setSettings(c.getSettings());
return;
}
}
throw new IllegalArgumentException("Could not find configuration with name " + configurationName);
}
@Override
public void copyTo(long recording, String path) throws IOException {
Objects.requireNonNull(path);
MBeanUtils.checkControl();
getExistingRecording(recording).dump(Paths.get(path));
}
@Override
public void setRecordingOptions(long recording, Map<String, String> options) throws IllegalArgumentException {
Objects.requireNonNull(options);
MBeanUtils.checkControl();
// Make local copy to prevent concurrent modification
Map<String, String> ops = new HashMap<String, String>(options);
for (Map.Entry<String, String> entry : ops.entrySet()) {
Object key = entry.getKey();
Object value = entry.getValue();
if (!(key instanceof String)) {
throw new IllegalArgumentException("Option key must not be null, or other type than " + String.class);
}
if (!OPTIONS.contains(key)) {
throw new IllegalArgumentException("Unknown recording option: " + key + ". Valid options are " + OPTIONS + ".");
}
if (value != null && !(value instanceof String)) {
throw new IllegalArgumentException("Incorrect value for option " + key + ". Values must be of type " + String.class + " .");
}
}
Recording r = getExistingRecording(recording);
validateOption(ops, OPTION_DUMP_ON_EXIT, MBeanUtils::booleanValue);
validateOption(ops, OPTION_DISK, MBeanUtils::booleanValue);
validateOption(ops, OPTION_NAME, Function.identity());
validateOption(ops, OPTION_MAX_AGE, MBeanUtils::duration);
validateOption(ops, OPTION_MAX_SIZE, MBeanUtils::size);
validateOption(ops, OPTION_DURATION, MBeanUtils::duration);
// All OK, now set them.atomically
setOption(ops, OPTION_DUMP_ON_EXIT, "false", MBeanUtils::booleanValue, x -> r.setDumpOnExit(x));
setOption(ops, OPTION_DISK, "true", MBeanUtils::booleanValue, x -> r.setToDisk(x));
setOption(ops, OPTION_NAME, String.valueOf(r.getId()), Function.identity(), x -> r.setName(x));
setOption(ops, OPTION_MAX_AGE, null, MBeanUtils::duration, x -> r.setMaxAge(x));
setOption(ops, OPTION_MAX_SIZE, "0", MBeanUtils::size, x -> r.setMaxSize(x));
setOption(ops, OPTION_DURATION, null, MBeanUtils::duration, x -> r.setDuration(x));
}
@Override
public Map<String, String> getRecordingOptions(long recording) throws IllegalArgumentException {
MBeanUtils.checkMonitor();
Recording r = getExistingRecording(recording);
Map<String, String> options = new HashMap<>(10);
options.put(OPTION_DUMP_ON_EXIT, String.valueOf(r.getDumpOnExit()));
options.put(OPTION_DISK, String.valueOf(r.isToDisk()));
options.put(OPTION_NAME, String.valueOf(r.getName()));
options.put(OPTION_MAX_AGE, ManagementSupport.formatTimespan(r.getMaxAge(), " "));
Long maxSize = r.getMaxSize();
options.put(OPTION_MAX_SIZE, String.valueOf(maxSize == null ? "0" : maxSize.toString()));
options.put(OPTION_DURATION, ManagementSupport.formatTimespan(r.getDuration(), " "));
return options;
}
@Override
public long cloneRecording(long id, boolean stop) throws IllegalStateException, SecurityException {
MBeanUtils.checkControl();
return getRecording(id).copy(stop).getId();
}
@Override
public ObjectName getObjectName() {
return MBeanUtils.createObjectName();
}
private Recording getExistingRecording(long id) {
if (FlightRecorder.isInitialized()) {
Recording recording = getRecording(id);
if (recording != null) {
return recording;
}
}
throw new IllegalArgumentException("No recording available with id " + id);
}
private Recording getRecording(long id) {
List<Recording> recs = getRecorder().getRecordings();
return recs.stream().filter(r -> r.getId() == id).findFirst().orElse(null);
}
private static <T, U> void setOption(Map<String, String> options, String name, String defaultValue, Function<String, U> converter, Consumer<U> setter) {
if (!options.containsKey(name)) {
return;
}
String v = options.get(name);
if (v == null) {
v = defaultValue;
}
try {
setter.accept(converter.apply(v));
} catch (IllegalArgumentException iae) {
throw new IllegalArgumentException("Not a valid value for option '" + name + "'. " + iae.getMessage());
}
}
private static <T, U> void validateOption(Map<String, String> options, String name, Function<String, U> validator) {
try {
String v = options.get(name);
if (v == null) {
return; // OK, will set default
}
validator.apply(v);
} catch (IllegalArgumentException iae) {
throw new IllegalArgumentException("Not a valid value for option '" + name + "'. " + iae.getMessage());
}
}
private FlightRecorder getRecorder() throws SecurityException {
// Synchronize on some private object that is always available
synchronized (streamHandler) {
if (recorder == null) {
recorder = AccessController.doPrivileged(new PrivilegedAction<FlightRecorder>() {
@Override
public FlightRecorder run() {
return FlightRecorder.getFlightRecorder();
}
}, null, new FlightRecorderPermission("accessFlightRecorder"));
}
return recorder;
}
}
private static MBeanNotificationInfo[] createNotificationInfo() {
String[] types = new String[] { AttributeChangeNotification.ATTRIBUTE_CHANGE };
String name = AttributeChangeNotification.class.getName();
String description = "Notifies if the RecordingState has changed for one of the recordings, for example if a recording starts or stops";
MBeanNotificationInfo info = new MBeanNotificationInfo(types, name, description);
return new MBeanNotificationInfo[] { info };
}
@Override
public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) {
MXBeanListener mxbeanListener = new MXBeanListener(listener, filter, handback);
listeners.add(mxbeanListener);
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run(){
FlightRecorder.addListener(mxbeanListener);
return null;
}
}, null, new FlightRecorderPermission("accessFlightRecorder"));
super.addNotificationListener(listener, filter, handback);
}
@Override
public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException {
removeListeners( x -> listener == x.listener);
super.removeNotificationListener(listener);
}
@Override
public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException {
removeListeners( x -> listener == x.listener && filter == x.filter && handback == x.handback);
super.removeNotificationListener(listener, filter, handback);
}
private void removeListeners(Predicate<MXBeanListener> p) {
List<MXBeanListener> toBeRemoved = new ArrayList<>(listeners.size());
for (MXBeanListener l : listeners) {
if (p.test(l)) {
toBeRemoved.add(l);
FlightRecorder.removeListener(l);
}
}
listeners.removeAll(toBeRemoved);
}
private Notification createNotication(Recording recording) {
try {
Long id = recording.getId();
Object oldValue = changes.get(recording.getId());
Object newValue = getAttribute(ATTRIBUTE_RECORDINGS);
if (recording.getState() != RecordingState.CLOSED) {
changes.put(id, newValue);
} else {
changes.remove(id);
}
return new AttributeChangeNotification(getObjectName(), sequenceNumber.incrementAndGet(), System.currentTimeMillis(), "Recording " + recording.getName() + " is "
+ recording.getState(), ATTRIBUTE_RECORDINGS, newValue.getClass().getName(), oldValue, newValue);
} catch (AttributeNotFoundException | MBeanException | ReflectionException e) {
throw new RuntimeException("Could not create notifcation for FlightRecorderMXBean. " + e.getMessage(), e);
}
}
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.management.jfr;
import java.lang.management.ManagementPermission;
import java.security.Permission;
import java.time.DateTimeException;
import java.time.Duration;
import java.time.Instant;
import java.time.format.DateTimeParseException;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import jdk.jfr.internal.management.ManagementSupport;
final class MBeanUtils {
private static final Permission monitor = new ManagementPermission("monitor");
private static final Permission control = new ManagementPermission("control");
static ObjectName createObjectName() {
try {
return new ObjectName(FlightRecorderMXBean.MXBEAN_NAME);
} catch (MalformedObjectNameException mne) {
throw new Error("Can't happen", mne);
}
}
static void checkControl() {
SecurityManager secManager = System.getSecurityManager();
if (secManager != null) {
secManager.checkPermission(control);
}
}
static void checkMonitor() {
SecurityManager secManager = System.getSecurityManager();
if (secManager != null) {
secManager.checkPermission(monitor);
}
}
static <T, R> List<R> transformList(List<T> source, Function<T, R> function) {
return source.stream().map(function).collect(Collectors.toList());
}
static boolean booleanValue(String s) {
if ("true".equals(s)) {
return true;
}
if ("false".equals(s)) {
return false;
}
throw new IllegalArgumentException("Value must be true or false.");
}
static Duration duration(String s) throws NumberFormatException {
if (s == null) {
return null;
}
long l = ManagementSupport.parseTimespan(s);
if (l == 0) {
return null;
}
return Duration.ofNanos(l);
}
public static Instant parseTimestamp(String s, Instant defaultValue) {
if (s == null) {
return defaultValue;
}
try {
return Instant.parse(s);
} catch(DateTimeParseException e ) {
// OK, try with milliseconds since epoch
// before giving up.
}
try {
return Instant.ofEpochMilli(Long.parseLong(s));
} catch (NumberFormatException | DateTimeException nfr) {
throw new IllegalArgumentException("Not a valid timestamp " + s);
}
}
static Long size(String s) throws NumberFormatException {
long size = Long.parseLong(s);
if (size < 0) {
throw new IllegalArgumentException("Negative size not allowed");
}
return size;
}
public static int parseBlockSize(String string, int defaultSize) {
if (string == null) {
return defaultSize;
}
int size = Integer.parseInt(string);
if (size <1) {
throw new IllegalArgumentException("Block size must be at least 1 byte");
}
return size;
}
}

View File

@@ -0,0 +1,397 @@
/*
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.management.jfr;
import java.time.Duration;
import java.time.Instant;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.TabularData;
import jdk.jfr.Recording;
import jdk.jfr.RecordingState;
import jdk.jfr.internal.management.ManagementSupport;
/**
* Management representation of a {@code Recording}.
*
* @see Recording
*
* @since 8
*/
public final class RecordingInfo {
private final long id;
private final String name;
private final String state;
private final boolean dumpOnExit;
private final long size;
private final boolean disk;
private final long maxAge;
private final long maxSize;
private final long startTime;
private final long stopTime;
private final String destination;
private final long durationInSeconds;
private final Map<String, String> settings;
// package private
RecordingInfo(Recording recording) {
id = recording.getId();
name = recording.getName();
state = recording.getState().toString();
dumpOnExit = recording.getDumpOnExit();
size = recording.getSize();
disk = recording.isToDisk();
Duration d = recording.getMaxAge();
if (d == null) {
maxAge = 0;
} else {
maxAge = d.getSeconds();
}
maxSize = recording.getMaxSize();
Instant s = recording.getStartTime();
startTime = s == null ? 0L : s.toEpochMilli();
Instant st = recording.getStopTime();
stopTime = st == null ? 0L : st.toEpochMilli();
destination = ManagementSupport.getDestinationOriginalText(recording);
Duration duration = recording.getDuration();
durationInSeconds = duration == null ? 0 : duration.getSeconds();
settings = recording.getSettings();
}
private RecordingInfo(CompositeData cd) {
id = (int) 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");
maxAge = (Long) cd.get("maxAge");
maxSize = (Long) cd.get("maxSize");
startTime = (Long) cd.get("startTime");
stopTime = (Long) cd.get("stopTime");
destination = (String) cd.get("destination");
durationInSeconds = (long) cd.get("duration");
settings = new LinkedHashMap<>();
Object map = cd.get("settings");
if (map instanceof TabularData) {
TabularData td = (TabularData) map;
List<String> keyNames = td.getTabularType().getIndexNames();
int size = keyNames.size();
for (Object keys : td.keySet()) {
Object[] keyValues = ((List<?>) keys).toArray();
for (int i = 0; i < size; i++) {
String key = keyNames.get(i);
Object value = keyValues[i];
if (value instanceof String) {
settings.put(key, (String) value);
}
}
}
}
}
/**
* Returns the name of the recording associated with this
* {@code RecordingInfo}.
*
* @return the recording name, not {@code null}
*
* @see Recording#getName()
*/
public String getName() {
return name;
}
/**
* Returns the unique ID for the recording associated with this
* {@code RecordingInfo}.
*
* @return the recording ID
*
* @see Recording#getId()
*/
public long getId() {
return id;
}
/**
* Returns if the recording associated with this {@code RecordingInfo}
* should be dumped to file when the JVM exits.
*
* @return {@code true} if recording should be dumped on exit, {@code false}
* otherwise
*
* @see Recording#getDumpOnExit()
*/
public boolean getDumpOnExit() {
return dumpOnExit;
}
/**
* Returns how many seconds data should be kept on disk, or {@code 0} if
* data is to be kept forever.
* <p>
* In-memory recordings are not affected by maximum age.
*
* @see Recording#getMaxAge()
* @see Recording#setToDisk(boolean)
* @return how long data should be kept on disk, measured in seconds
*
*/
public long getMaxAge() {
return maxAge;
}
/**
* Returns the amount of data, measured in bytes, the recording associated
* with this {@code RecordingInfo}, should be kept on disk, before it's
* rotated away, or {@code 0} if data is to be kept indefinitely.
* <p>
* In-memory recordings are not affected by maximum size.
*
* @return the amount of data should be kept on disk, in bytes
*
* @see Recording#setToDisk(boolean)
* @see Recording#getMaxSize()
*/
public long getMaxSize() {
return maxSize;
}
/**
* Returns a {@code String} representation of state of the recording
* associated with this {@code RecordingInfo}.
* <p>
* Valid return values are {@code "NEW"}, {@code "DELAYED"}, {@code "STARTING"},
* {@code "RUNNING"}, {@code "STOPPING"}, {@code "STOPPED"} and {@code "CLOSED"}.
*
* @return the recording state, not {@code null}
*
* @see RecordingState#toString()
* @see Recording#getState()
*/
public String getState() {
return state;
}
/**
* Returns start time of the recording associated with this
* {@code RecordingInfo}, measured as ms since epoch, or {@code null} if the
* recording hasn't started.
*
* @return the start time of the recording, or {@code null} if the recording
* hasn't started
*
* @see Recording#getStartTime()
*/
public long getStartTime() {
return startTime;
}
/**
* Returns the actual or expected stop time of the recording associated with
* this {@code RecordingInfo}, measured as ms since epoch, or {@code null}
* if the expected or actual stop time is not known, which can only happen
* if the recording has not yet been stopped.
*
* @return the stop time of recording, or {@code null} if recording hasn't
* been stopped.
*
* @see Recording#getStopTime()
*/
public long getStopTime() {
return stopTime;
}
/**
* Returns the settings for the recording associated with this
* {@code RecordingInfo}.
*
* @return the recording settings, not {@code null}
*
* @see Recording#getSettings()
*/
public Map<String, String> getSettings() {
return settings;
}
/**
* Returns destination path where data, for the recording associated with
* this {@link RecordingInfo}, should be written when the recording stops,
* or {@code null} if the recording should not be written.
*
* @return the destination, or {@code null} if not set
*
* @see Recording#getDestination()
*/
public String getDestination() {
return destination;
}
/**
* Returns a string description of the recording associated with this
* {@code RecordingInfo}
*
* @return description, not {@code null}
*/
@Override
public String toString() {
Stringifier s = new Stringifier();
s.add("name", name);
s.add("id", id);
s.add("maxAge", maxAge);
s.add("maxSize", maxSize);
return s.toString();
}
/**
* Returns the amount data recorded by recording. associated with this
* {@link RecordingInfo}.
*
* @return the amount of recorded data, measured in bytes
*/
public long getSize() {
return size;
}
/**
* Returns {@code true} if the recording associated with this
* {@code RecordingInfo} should be flushed to disk, when memory buffers are
* full, {@code false} otherwise.
*
* @return {@code true} if recording is to disk, {@code false} otherwise
*/
public boolean isToDisk() {
return disk;
}
/**
* Returns the desired duration, measured in seconds, of the recording
* associated with this {@link RecordingInfo}, or {code 0} if no duration
* has been set.
*
* @return the desired duration, or {code 0} if no duration has been set
*
* @see Recording#getDuration()
*/
public long getDuration() {
return durationInSeconds;
}
/**
* Returns a {@code RecordingInfo} represented by the specified
* {@code CompositeData} object.
* <p>
* The specified {@code CompositeData} must have the following item names and
* item types to be valid. <blockquote>
* <table class="striped">
* <caption>Supported names and types in a specified {@code CompositeData} object</caption>
* <thead>
* <tr>
* <th scope="col" style="text-align:left">Name</th>
* <th scope="col" style="text-align:left">Type</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <th scope="row">id</th>
* <td>{@code Long}</td>
* </tr>
* <tr>
* <th scope="row">name</th>
* <td>{@code String}</td>
* </tr>
* <tr>
* <th scope="row">state</th>
* <td>{@code String}</td>
* </tr>
* <tr>
* <th scope="row">dumpOnExit</th>
* <td>{@code Boolean}</td>
* </tr>
* <tr>
* <th scope="row">size</th>
* <td>{@code Long}</td>
* </tr>
* <tr>
* <th scope="row">disk</th>
* <td>{@code Boolean}</td>
* </tr>
* <tr>
* <th scope="row">maxAge</th>
* <td>{@code Long}</td>
* </tr>
* <tr>
* <th scope="row">maxSize</th>
* <td>{@code Long}</td>
* </tr>
* <tr>
* <th scope="row">startTime</th>
* <td>{@code Long}</td>
* </tr>
* <tr>
* <th scope="row">stopTime</th>
* <td>{@code Long}</td>
* </tr>
* <tr>
* <th scope="row">destination</th>
* <td>{@code String}</td>
* </tr>
* <tr>
* <th scope="row">duration</th>
* <td>{@code Long}</td>
* </tr>
* <tr>
* <th scope="row">settings</th>
* <td>{@code javax.management.openmbean.CompositeData[]} whose element type
* is the mapped type for {@link SettingDescriptorInfo} as specified in the
* {@link SettingDescriptorInfo#from} method.</td>
* </tr>
* </tbody>
* </table>
* </blockquote>
*
* @param cd {@code CompositeData} representing the {@code RecordingInfo} to
* return
*
* @throws IllegalArgumentException if {@code cd} does not represent a valid
* {@code RecordingInfo}
*
* @return the {@code RecordingInfo} represented by {@code cd}, or
* {@code null} if {@code cd} is {@code null}
*/
public static RecordingInfo from(CompositeData cd) {
if (cd == null) {
return null;
}
return new RecordingInfo(cd);
}
}

View File

@@ -0,0 +1,232 @@
/*
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.management.jfr;
import java.util.concurrent.Callable;
import javax.management.openmbean.CompositeData;
import jdk.jfr.EventType;
import jdk.jfr.SettingDescriptor;
import jdk.management.jfr.internal.FlightRecorderMXBeanProvider;
/**
* Management class that describes a setting, for example name, description and
* default value.
*
* @see EventType#getSettingDescriptors()
*
* @since 8
*/
public final class SettingDescriptorInfo {
// Purpose of this static initializer is to allow
// FlightRecorderMXBeanProvider
// to be in an internal package and not visible, but at the same time allow
// it to instantiate FlightRecorderMXBeanImpl.
//
// The reason the mechanism is in this class is because it is light weight
// and can easily be triggered from FlightRecorderMXBeanProvider.
static {
FlightRecorderMXBeanProvider.setFlightRecorderMXBeanFactory(new Callable<FlightRecorderMXBean>() {
@Override
public FlightRecorderMXBean call() throws Exception {
return new FlightRecorderMXBeanImpl();
}
});
}
private final String name;
private final String label;
private final String description;
private final String typeName;
private final String contentType;
private final String defaultValue;
// package private
SettingDescriptorInfo(SettingDescriptor settingDescriptor) {
this.name = settingDescriptor.getName();
this.label = settingDescriptor.getLabel();
this.description = settingDescriptor.getDescription();
this.typeName = settingDescriptor.getTypeName();
this.contentType = settingDescriptor.getContentType();
this.defaultValue = settingDescriptor.getDefaultValue();
}
private SettingDescriptorInfo(CompositeData cd) {
this.name = (String) cd.get("name");
this.label = (String) cd.get("label");
this.description = (String) cd.get("description");
this.typeName = (String) cd.get("typeName");
this.defaultValue = (String) cd.get("defaultValue");
this.contentType = (String) cd.get("contentType");
}
/**
* Returns the human-readable name of the setting associated with this
* {@code SettingDescriptorInfo} (for example, {@code "Threshold"}).
*
* @return the label for this setting, not {@code null}
*/
public String getLabel() {
return label;
}
/**
* Returns the name of the setting associated with this
* {@code SettingDescriptorInfo} (for example, {@code "threshold"}).
*
* @return the name of this setting, not {@code null}
*/
public String getName() {
return name;
}
/**
* Returns the description of the setting associated this
* {@code SettingDescriptorInfo} (for example,
* {@code "The duration an event must exceed to be be recorded"}).
*
* @return the description of this setting, not null
*/
public String getDescription() {
return description;
}
/**
* Returns the type name of the setting associated this
* {@code SettingDescriptorInfo} (for example,
* {@code "jdk.settings.Threshold"}).
* <p>
* The type can be used to identify what type of setting this is.
*
* @return the name of this settings type, not {@code null}
*/
public String getTypeName() {
return typeName;
}
/**
* Returns the content type of the setting associated this
* {@code SettingDescriptorInfo} (for example, {@code "jdk.jfr.Timespan"}).
* <p>
* The content type can be used to determine how the setting should be
* rendered in a graphical user interface.
*
* @return the name of this settings type, not {@code null}
*/
public String getContentType() {
return contentType;
}
/**
* Returns the default value of the setting associated this
* {@code SettingDescriptorInfo} (for example, {@code "20 ms"}).
*
* @return default value for this setting, not {@code null}
*
* @see SettingDescriptor#getDefaultValue()
*/
public String getDefaultValue() {
return defaultValue;
}
/**
* Returns an {@code SettingDescriptorInfo} represented by the specified
* {@code CompositeData}
* <p>
* The supplied {@code CompositeData} must have the following item names and
* item types to be valid. <blockquote>
* <table class="striped">
* <caption>The name and type the specified CompositeData must contain</caption>
* <thead>
* <tr>
* <th scope="col" style="text-align:left">Name</th>
* <th scope="col" style="text-align:left">Type</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <th scope="row">name</th>
* <td>{@code String}</td>
* </tr>
* <tr>
* <th scope="row">label</th>
* <td>{@code String}</td>
* </tr>
* <tr>
* <th scope="row">description</th>
* <td>{@code String}</td>
* </tr>
* <tr>
* <th scope="row">typeName</th>
* <td>{@code String}</td>
* </tr>
* <tr>
* <th scope="row">contentType</th>
* <td>{@code String}</td>
* </tr>
* <tr>
* <th scope="row">defaultValue</th>
* <td>{@code String}</td>
* </tr>
* </tbody>
* </table>
* </blockquote>
*
* @param cd {@code CompositeData} representing the {@code SettingDescriptorInfo} to
* return
*
* @throws IllegalArgumentException if {@code cd} does not represent a valid
* {@code EventTypeInfo}
*
* @return a {@code SettingDescriptorInfo}, or {@code null} if {@code cd} is
* {@code null}
*/
public static SettingDescriptorInfo from(CompositeData cd) {
if (cd == null) {
return null;
}
return new SettingDescriptorInfo(cd);
}
/**
* Returns a {@code String} description of this {@code SettingDescriptorInfo}.
*
* @return a string describing this setting, not {@code null}
*/
@Override
public String toString() {
Stringifier s = new Stringifier();
s.add("name", name);
s.add("label", label);
s.add("description", description);
s.add("typeName", typeName);
s.add("contentType", contentType);
s.add("defaultValue", defaultValue);
return s.toString();
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.management.jfr;
import java.io.BufferedInputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
final class Stream implements Closeable {
private final long identifier;
private final BufferedInputStream inputStream;
private final byte[] buffer;
private volatile long time;
Stream(InputStream is, long identifier, int blockSize) {
this.inputStream = new BufferedInputStream(is, 50000);
this.identifier = identifier;
this.buffer = new byte[blockSize];
}
private void touch() {
time = System.currentTimeMillis();
}
public long getLastTouched() {
return time;
}
public byte[] read() throws IOException {
// OK to reuse buffer since this
// is only used for serialization
touch();
int read = inputStream.read(buffer);
if (read == -1) {
// null indicate no more data
return null;
}
if (read != buffer.length) {
byte[] smallerBuffer = new byte[read];
System.arraycopy(buffer, 0, smallerBuffer, 0, read);
return smallerBuffer;
}
return buffer;
}
@Override
public void close() throws IOException {
inputStream.close();
}
public long getId() {
return identifier;
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.management.jfr;
import java.util.TimerTask;
final class StreamCleanupTask extends TimerTask {
private final Stream stream;
private final StreamManager manager;
StreamCleanupTask(StreamManager streamManager, Stream stream) {
this.stream = stream;
this.manager = streamManager;
}
@Override
public void run() {
long lastTouched = stream.getLastTouched();
long now = System.currentTimeMillis();
if (now - lastTouched >= StreamManager.TIME_OUT) {
manager.destroy(stream);
} else {
manager.scheduleAbort(stream, lastTouched + StreamManager.TIME_OUT);
}
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.management.jfr;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.concurrent.TimeUnit;
final class StreamManager {
public static final long TIME_OUT = TimeUnit.MINUTES.toMillis(2);
public static final int DEFAULT_BLOCK_SIZE = 50000;
private static long idCounter = 0;
private final Map<Long, Stream> streams = new HashMap<>();
private Timer timer;
public synchronized Stream getStream(long streamIdentifer) {
Stream stream = streams.get(streamIdentifer);
if (stream == null) {
throw new IllegalArgumentException("Unknown stream identifier " + streamIdentifer);
}
return stream;
}
public synchronized Stream create(InputStream is, int blockSize) {
idCounter++;
Stream stream = new Stream(is, idCounter, blockSize);
streams.put(stream.getId(), stream);
scheduleAbort(stream, System.currentTimeMillis() + TIME_OUT);
return stream;
}
public synchronized void destroy(Stream stream) {
try {
stream.close();
} catch (IOException e) {
// OK
}
streams.remove(stream.getId());
if (streams.isEmpty()) {
timer.cancel();
timer = null;
}
}
public synchronized void scheduleAbort(Stream s, long when) {
if (timer == null) {
timer = new Timer(true);
}
timer.schedule(new StreamCleanupTask(this, s), new Date(when + TIME_OUT));
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.management.jfr;
/**
* Helper class for generating toString()
*
*/
final class Stringifier {
private final StringBuilder sb = new StringBuilder();
private boolean first = true;
public void add(String name, Object value) {
if (first) {
first = false;
} else {
sb.append(" ");
}
boolean isString = value instanceof String;
sb.append(name).append("=");
if (value == null) {
sb.append("null");
} else {
if (isString) {
sb.append("\"");
}
sb.append(value);
if (isString) {
sb.append("\"");
}
}
}
@Override
public String toString() {
return sb.toString();
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.management.jfr.internal;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import jdk.jfr.internal.management.ManagementSupport;
import jdk.management.jfr.FlightRecorderMXBean;
import jdk.management.jfr.SettingDescriptorInfo;
// XXX TODO
//import sun.management.spi.PlatformMBeanProvider;
public final class FlightRecorderMXBeanProvider /*extends PlatformMBeanProvider */{
private final static class SingleMBeanComponent
/*implements PlatformComponent<FlightRecorderMXBean>*/ {
private final String objectName;
private final Class<FlightRecorderMXBean> mbeanInterface;
public SingleMBeanComponent(String objectName,
Class<FlightRecorderMXBean> mbeanInterface) {
this.objectName = objectName;
this.mbeanInterface = mbeanInterface;
}
// @Override
// public Set<String> mbeanInterfaceNames() {
// return Collections.singleton(mbeanInterface.getName());
// }
//
// @Override
// public Map<String, FlightRecorderMXBean> nameToMBeanMap() {
// FlightRecorderMXBean bean = getFlightRecorderMXBean();
// if (bean != null) {
// return Collections.singletonMap(objectName, bean);
// } else {
// return Collections.emptyMap();
// }
// }
//
// @Override
// public String getObjectNamePattern() {
// return objectName;
// }
//
// @Override
// public Set<Class<? extends FlightRecorderMXBean>> mbeanInterfaces() {
// return Collections.singleton(mbeanInterface);
// }
}
private static Callable<FlightRecorderMXBean> flightRecorderMXBeanFactory;
private static volatile FlightRecorderMXBean flightRecorderMXBean;
public static FlightRecorderMXBean getFlightRecorderMXBean() {
FlightRecorderMXBean bean = flightRecorderMXBean;
if (bean == null) {
SettingDescriptorInfo.from(null); // Sets flightRecorderMXBeanFactory under <clinit> lock
synchronized (flightRecorderMXBeanFactory) {
bean = flightRecorderMXBean;
if (bean != null) {
return bean;
}
try {
bean = flightRecorderMXBean = flightRecorderMXBeanFactory.call();
} catch (Exception e) {
ManagementSupport.logError("Could not create Flight Recorder "
+ "instance for MBeanServer. " + e.getMessage());
}
}
}
return bean;
}
public static void setFlightRecorderMXBeanFactory(Callable<FlightRecorderMXBean> factory) {
flightRecorderMXBeanFactory = factory;
}
// @Override
// public List<PlatformComponent<?>> getPlatformComponentList() {
// String objectName = FlightRecorderMXBean.MXBEAN_NAME;
// Class<FlightRecorderMXBean> mbeanInterface = FlightRecorderMXBean.class;
// return Collections.singletonList(new SingleMBeanComponent(objectName, mbeanInterface));
// }
}