Add ability to work on older Java 8 JVMs (#13)

* Add ability to work on older Java 8 JVMs

* add FlightRecorderConnectionJava8Test

* fix test name

* recommended changes to pr 13

Co-authored-by: David Grieve <david.grieve@microsoft.com>
This commit is contained in:
John Oliver 2021-05-13 13:30:50 +01:00 коммит произвёл GitHub
Родитель d61f9f2443
Коммит 8b4abb0503
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
11 изменённых файлов: 394 добавлений и 49 удалений

Просмотреть файл

@ -94,6 +94,12 @@
<version>7.3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-testng</artifactId>
<version>0.3.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>

Просмотреть файл

@ -1,13 +1,5 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
package com.microsoft.jfr;
import java.io.IOException;
import java.io.InputStream;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanServerConnection;
@ -24,6 +16,12 @@ import javax.management.openmbean.SimpleType;
import javax.management.openmbean.TabularData;
import javax.management.openmbean.TabularDataSupport;
import javax.management.openmbean.TabularType;
import java.io.IOException;
import java.io.InputStream;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* Represents a connection to a {@code jdk.management.jfr.FlightRecorderMXBean} of a JVM.
@ -42,7 +40,6 @@ import javax.management.openmbean.TabularType;
* also be to a remote MBean server via {@code javax.management.remote.JMXConnector}.
* Refer to the summary in the javadoc of the {@code javax.management} package and of the
* {@code javax.management.remote} package for details.
*
*/
public class FlightRecorderConnection {
@ -54,19 +51,19 @@ public class FlightRecorderConnection {
* indicates a problem with the connection to the MBean server. An {@code InstanceNotFoundException}
* indicates that the FlightRecorder MBean is not registered on the target JVM. This could happen
* if the target JVM does not support Java Flight Recorder, or if experimental features need to be
* enabled on the target JVM.
* enabled on the target JVM. If an {@code InstanceNotFoundException} is thrown by a Java 8 JVM,
* consider using {@link com.microsoft.jfr.dcmd.FlightRecorderDiagnosticCommandConnection}.
*
* @param mBeanServerConnection The {@code MBeanServerConnection} to the JVM.
* @return A {@code FlightRecorderConnection}.
* @throws IOException A communication problem occurred when talking to the MBean server.
* @throws IOException A communication problem occurred when talking to the MBean server.
* @throws InstanceNotFoundException The FlightRecorder MBean is not registered on the target JVM.
* @throws JfrStreamingException Wraps a {@code javax.management.MalformedObjectNameException}
* and indicates a bug in this class.
* @throws NullPointerException The {@code mBeanServerConnection} parameter is {@code null}.
* @throws JfrStreamingException Wraps a {@code javax.management.MalformedObjectNameException}
* and indicates a bug in this class.
* @throws NullPointerException The {@code mBeanServerConnection} parameter is {@code null}.
*/
public static FlightRecorderConnection connect(MBeanServerConnection mBeanServerConnection)
throws IOException, InstanceNotFoundException, JfrStreamingException
{
throws IOException, InstanceNotFoundException, JfrStreamingException {
Objects.requireNonNull(mBeanServerConnection);
try {
ObjectName objectName = new ObjectName(JFR_OBJECT_NAME);
@ -108,20 +105,20 @@ public class FlightRecorderConnection {
* The cause may also be a {@code javax.management.openmbean.OpenDataException}
* which indicates a bug in the code of this class.
*/
/* package-scoped */ long startRecording(RecordingOptions recordingOptions, RecordingConfiguration recordingConfiguration)
public long startRecording(RecordingOptions recordingOptions, RecordingConfiguration recordingConfiguration)
throws IOException, JfrStreamingException {
try {
Object[] args = new Object[]{};
String[] argTypes = new String[]{};
final long id = (long) connection.invoke(flightRecorder, "newRecording", args, argTypes);
final long id = (long) mBeanServerConnection.invoke(objectName, "newRecording", args, argTypes);
if (recordingConfiguration != null) {
String configuration = recordingConfiguration.getConfiguration();
if (configuration != null && configuration.trim().length() > 0) {
args = new Object[]{id, configuration};
argTypes = new String[]{long.class.getName(), String.class.getName()};
connection.invoke(flightRecorder, recordingConfiguration.getMbeanSetterFunction(), args, argTypes);
mBeanServerConnection.invoke(objectName, recordingConfiguration.getMbeanSetterFunction(), args, argTypes);
}
}
@ -131,16 +128,16 @@ public class FlightRecorderConnection {
TabularData recordingOptionsParam = makeOpenData(options);
args = new Object[]{id, recordingOptionsParam};
argTypes = new String[]{long.class.getName(), TabularData.class.getName()};
connection.invoke(flightRecorder, "setRecordingOptions", args, argTypes);
mBeanServerConnection.invoke(objectName, "setRecordingOptions", args, argTypes);
}
}
args = new Object[]{id};
argTypes = new String[]{long.class.getName()};
connection.invoke(flightRecorder, "startRecording", args, argTypes);
mBeanServerConnection.invoke(objectName, "startRecording", args, argTypes);
return id;
} catch (OpenDataException|InstanceNotFoundException|MBeanException|ReflectionException e) {
} catch (OpenDataException |InstanceNotFoundException| MBeanException | ReflectionException e) {
// In theory, we should never get these.
throw new JfrStreamingException(e.getMessage(), e);
}
@ -154,11 +151,11 @@ public class FlightRecorderConnection {
* a {@code javax.management.MBeanException} or a {@code javax.management.ReflectionException}
* and indicates an issue with the FlightRecorderMXBean in the JVM.
*/
/* package-scoped */ void stopRecording(long id) throws IOException, JfrStreamingException {
public void stopRecording(long id) throws IOException, JfrStreamingException {
try {
Object[] args = new Object[]{id};
String[] argTypes = new String[]{long.class.getName()};
connection.invoke(flightRecorder, "stopRecording", args, argTypes);
mBeanServerConnection.invoke(objectName, "stopRecording", args, argTypes);
} catch (InstanceNotFoundException|MBeanException|ReflectionException e) {
throw new JfrStreamingException(e.getMessage(), e);
}
@ -172,11 +169,11 @@ public class FlightRecorderConnection {
* @throws IOException A communication problem occurred when talking to the MBean server.
* @throws JfrStreamingException Wraps a {@code javax.management.JMException}.
*/
/* package-scoped */ void dumpRecording(long id, String outputFile) throws IOException, JfrStreamingException {
public void dumpRecording(long id, String outputFile) throws IOException, JfrStreamingException {
try {
Object[] args = new Object[]{id,outputFile};
String[] argTypes = new String[]{long.class.getName(),String.class.getName()};
connection.invoke(flightRecorder, "copyTo", args, argTypes);
mBeanServerConnection.invoke(objectName, "copyTo", args, argTypes);
} catch (InstanceNotFoundException|MBeanException|ReflectionException e) {
throw new JfrStreamingException(e.getMessage(), e);
}
@ -190,19 +187,20 @@ public class FlightRecorderConnection {
* @param stop Whether to stop the cloned recording.
* @throws IOException A communication problem occurred when talking to the MBean server.
* @throws JfrStreamingException Wraps a {@code javax.management.JMException}.
* @return id of the recording
*/
/* package-scoped */ long cloneRecording(long id, boolean stop) throws IOException, JfrStreamingException {
public long cloneRecording(long id, boolean stop) throws IOException, JfrStreamingException {
try {
Object[] args = new Object[]{id,stop};
String[] argTypes = new String[]{long.class.getName(),boolean.class.getName()};
return (long)connection.invoke(flightRecorder, "cloneRecording", args, argTypes);
return (long) mBeanServerConnection.invoke(objectName, "cloneRecording", args, argTypes);
} catch (InstanceNotFoundException|MBeanException|ReflectionException e) {
throw new JfrStreamingException(e.getMessage(), e);
}
}
/**
* Get the Java Flight Recording as an {@code java.io.InputStream.
* Get the Java Flight Recording as an {@code java.io.InputStream}.
* This method is called from the {@link Recording#getStream(Instant, Instant, long)} method.
*
* The recording may contain data outside the {@code startTime} and {@code endTime} parameters.
@ -227,7 +225,7 @@ public class FlightRecorderConnection {
* The cause may also be a {@code javax.management.openmbean.OpenDataException}
* which indicates a bug in the code of this class.
*/
/* package-scoped */ InputStream getStream(long id, Instant startTime, Instant endTime, long blockSize)
public InputStream getStream(long id, Instant startTime, Instant endTime, long blockSize)
throws IOException, JfrStreamingException {
Map<String,String> options = new HashMap<>();
if (startTime != null) options.put("startTime", startTime.toString());
@ -238,8 +236,8 @@ public class FlightRecorderConnection {
TabularData streamOptions = makeOpenData(options);
Object[] args = new Object[]{id, streamOptions};
String[] argTypes = new String[]{long.class.getName(), TabularData.class.getName()};
long streamId = (long) connection.invoke(flightRecorder, "openStream", args, argTypes);
return new JfrStream(connection, flightRecorder, streamId);
long streamId = (long) mBeanServerConnection.invoke(objectName, "openStream", args, argTypes);
return new JfrStream(mBeanServerConnection, objectName, streamId);
} catch(OpenDataException|InstanceNotFoundException|MBeanException|ReflectionException e) {
throw new JfrStreamingException(e.getMessage(), e);
}
@ -253,23 +251,32 @@ public class FlightRecorderConnection {
* a {@code javax.management.MBeanException} or a {@code javax.management.ReflectionException}
* and indicates an issue with the FlightRecorderMXBean in the JVM.
*/
/* package-scoped */ void closeRecording(long id) throws IOException, JfrStreamingException {
public void closeRecording(long id) throws IOException, JfrStreamingException {
try {
Object[] args = new Object[]{id};
String[] argTypes = new String[]{long.class.getName()};
connection.invoke(flightRecorder, "closeRecording", args, argTypes);
mBeanServerConnection.invoke(objectName, "closeRecording", args, argTypes);
} catch (InstanceNotFoundException|MBeanException|ReflectionException e) {
throw new JfrStreamingException(e.getMessage(), e);
throw new JfrStreamingException(e.getMessage(), e);
}
}
private FlightRecorderConnection(MBeanServerConnection connection, ObjectName objectName) {
this.connection = connection;
this.flightRecorder = objectName;
/**
* Constructor is called from the static {@link FlightRecorderConnection#connect(MBeanServerConnection)}
* method, and from the {@link com.microsoft.jfr.dcmd.FlightRecorderDiagnosticCommandConnection#connect(MBeanServerConnection)}
* method. It is not possible to create a FlightRecorderConnection directly.
* @param mBeanServerConnection The connection to the MBeanServer.
* @param objectName The name of the MBean we are connected to.
*/
protected FlightRecorderConnection(MBeanServerConnection mBeanServerConnection, ObjectName objectName) {
this.mBeanServerConnection = mBeanServerConnection;
this.objectName = objectName;
}
private final MBeanServerConnection connection;
private final ObjectName flightRecorder;
/** The MBeanServerConnection. */
protected final MBeanServerConnection mBeanServerConnection;
/** The ObjectName of the MBean we are connecting to. */
protected final ObjectName objectName;
/**
* Convert the Map to TabularData
@ -293,5 +300,4 @@ public class FlightRecorderConnection {
}
return table;
}
}

Просмотреть файл

@ -38,7 +38,7 @@ class JfrStream extends InputStream {
private final MBeanServerConnection connection;
private final ObjectName flightRecorder;
JfrStream(MBeanServerConnection connection, ObjectName flightRecorder, long streamid) {
/* package scope */ JfrStream(MBeanServerConnection connection, ObjectName flightRecorder, long streamid) {
this.streamid = streamid;
this.connection = connection;
this.flightRecorder = flightRecorder;

Просмотреть файл

@ -1,7 +1,5 @@
package com.microsoft.jfr;
import javax.management.JMException;
/**
* An JfrStreamingException is a wrapper around specific {@code javax.management.JMException}
* instances which might be thrown, either directly or indirectly, by methods of this package.
@ -35,8 +33,20 @@ public class JfrStreamingException extends Exception {
private static final long serialVersionUID = 7394612902107510439L;
JfrStreamingException(String message, JMException cause) {
/**
* Construct a {@code JfrStreamingException} with a message and cause.
* @param message The exception message.
* @param cause The cause of the exception.
*/
public JfrStreamingException(String message, Exception cause) {
super(message, cause);
}
/**
* Construct a {@code JfrStreamingException} with a message only.
* @param message The exception message.
*/
public JfrStreamingException(String message) {
super(message);
}
}

Просмотреть файл

@ -83,7 +83,7 @@ public class Recording implements AutoCloseable {
* @param recordingOptions The options to be used for the recording
* @param recordingConfiguration The settings for events to be collected by the recording
*/
/* package-scoped */ Recording(
public Recording(
FlightRecorderConnection connection,
RecordingOptions recordingOptions,
RecordingConfiguration recordingConfiguration) {

Просмотреть файл

@ -1,6 +1,7 @@
package com.microsoft.jfr;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Objects;
@ -42,12 +43,14 @@ public abstract class RecordingConfiguration {
public PredefinedConfiguration(String configurationName) {
super(configurationName);
}
}
/**
* A configuration that is read from a jfc file
*/
public static class JfcFileConfiguration extends RecordingConfiguration {
/**
* Sets a configuration from a jfc file to use with a {@code Recording}.
*

Просмотреть файл

@ -16,7 +16,7 @@ import java.util.stream.Stream;
* use {@link Builder}.
* <p>
* The options must be set at the time the {@code Recording} is created via
* {@link FlightRecorderConnection#newRecording(RecordingOptions, RecordingConfiguration)}.
* {@link FlightRecorderConnection#newRecording(RecordingOptions, RecordingConfiguration)}.
* A {@code RecordingOptions} is immutable which prevents attempts at changing the options
* while a recording is in progress.
* <p>
@ -345,7 +345,7 @@ public class RecordingOptions {
* according to FlightRecorderMXBean.
* @return A read-only map of the recording options.
*/
/* package-scoped */ Map<String,String> getRecordingOptions() {
public Map<String,String> getRecordingOptions() {
return recordingOptions;
}

Просмотреть файл

@ -0,0 +1,239 @@
package com.microsoft.jfr.dcmd;
import com.microsoft.jfr.FlightRecorderConnection;
import com.microsoft.jfr.JfrStreamingException;
import com.microsoft.jfr.Recording;
import com.microsoft.jfr.RecordingConfiguration;
import com.microsoft.jfr.RecordingOptions;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import java.io.IOException;
import java.io.InputStream;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Represents a connection to the {@code com.sun.management DiagnosticCommand} MBean of a JVM.
* This is the same mechanism used by the Java <i>jcmd</i> tool.
* {@code FlightRecorderDiagnosticCommandConnection} provides
* {@link #newRecording(RecordingOptions, RecordingConfiguration) API} to create
* Java flight {@link Recording recordings}. More than one {@code Recording} can be created.
* <p>
* To use this class, a {@code javax.management.MBeanServerConnection} is needed.
* This class uses the connection to make calls to the MBean server and does not change
* the state of the connection. Management of the connection is the concern of the caller
* and use of a {@code FlightRecorderConnection} for an MBean server connection that is no
* longer valid will result in {@code IOException} being thrown.
* <p>
* The {@code MBeanServerConnection} can be a connection to any MBean server.
* Typically, the connection is to the platform MBean server obtained by calling
* {@code java.lang.management.ManagementFactory.getPlatformMBeanServer()}. The connection can
* also be to a remote MBean server via {@code javax.management.remote.JMXConnector}.
* Refer to the summary in the javadoc of the {@code javax.management} package and of the
* {@code javax.management.remote} package for details.
*
*/
public class FlightRecorderDiagnosticCommandConnection extends FlightRecorderConnection {
private static final String DIAGNOSTIC_COMMAND_OBJECT_NAME = "com.sun.management:type=DiagnosticCommand";
private static final String JFR_START_REGEX = "Started recording (.+?)\\. .*";
private static final Pattern JFR_START_PATTERN = Pattern.compile(JFR_START_REGEX, Pattern.DOTALL);
// All JFR commands take String[] parameters
private static final String[] signature = new String[]{"[Ljava.lang.String;"};
/**
* Create a connection to the {@code FlightRecorder} via JMX. This method either returns a
* {@code FlightRecorderConnection}, or throws an exception. An {@code IOException}
* indicates a problem with the connection to the MBean server. An {@code InstanceNotFoundException}
* indicates that the FlightRecorder MBean is not registered on the target JVM. This could happen
* if the target JVM does not support Java Flight Recorder, or if expermental features need to be
* enabled on the target JVM.
*
* @param mBeanServerConnection The {@code MBeanServerConnection} to the JVM.
* @return A {@code FlightRecorderConnection}.
* @throws IOException A communication problem occurred when talking to the MBean server.
* @throws InstanceNotFoundException The FlightRecorder MBean is not registered on the target JVM.
* @throws JfrStreamingException Wraps a {@code javax.management.MalformedObjectNameException}
* and indicates a bug in this class.
* @throws NullPointerException The {@code mBeanServerConnection} parameter is {@code null}.
*/
public static FlightRecorderConnection connect(MBeanServerConnection mBeanServerConnection)
throws IOException, InstanceNotFoundException, JfrStreamingException {
Objects.requireNonNull(mBeanServerConnection);
try {
ObjectInstance objectInstance = mBeanServerConnection.getObjectInstance(new ObjectName(DIAGNOSTIC_COMMAND_OBJECT_NAME));
ObjectName objectName = objectInstance.getObjectName();
assertCommercialFeaturesUnlocked(mBeanServerConnection, objectName);
return new FlightRecorderDiagnosticCommandConnection(mBeanServerConnection, objectInstance.getObjectName());
} catch (MalformedObjectNameException e) {
// Not expected to happen. This exception comes from the ObjectName constructor. If
// DIAGNOSTIC_COMMAND_OBJECT_NAME is malformed, then this is an internal bug.
throw new JfrStreamingException(DIAGNOSTIC_COMMAND_OBJECT_NAME, e);
}
}
/* package scope for testing */ FlightRecorderDiagnosticCommandConnection(MBeanServerConnection mBeanServerConnection, ObjectName objectName) {
super(mBeanServerConnection, objectName);
}
/**
* Start a recording. This method creates a new recording, sets the configuration, and then starts the recording.
* This method is called from the {@link Recording#start()} method.
*
* @param recordingOptions The {@code RecordingOptions} which was passed to
* the {@link #newRecording(RecordingOptions, RecordingConfiguration)} method
* @param recordingConfiguration The {@code RecordingConfiguration} which was passed to
* the {@link #newRecording(RecordingOptions, RecordingConfiguration)} method
* @return The id of the recording.
* @throws JfrStreamingException Wraps an {@code javax.management.InstanceNotFoundException},
* a {@code javax.management.MBeanException} or a {@code javax.management.ReflectionException}
* and indicates an issue with the FlightRecorderMXBean in the JVM.
* The cause may also be a {@code javax.management.openmbean.OpenDataException}
* which indicates a bug in the code of this class.
*/
@Override
public long startRecording(RecordingOptions recordingOptions, RecordingConfiguration recordingConfiguration) throws JfrStreamingException {
Object[] params = formOptions(recordingOptions, recordingConfiguration);
// jfrStart returns "Started recording 2." and some more stuff, but all we care about is the name of the recording.
try {
String jfrStart = (String) mBeanServerConnection.invoke(objectName, "jfrStart", params, signature);
String name;
Matcher matcher = JFR_START_PATTERN.matcher(jfrStart);
if (matcher.find()) {
name = matcher.group(1);
return Long.parseLong(name);
}
} catch (InstanceNotFoundException | IOException | ReflectionException | MBeanException e) {
throw new JfrStreamingException("Failed to start recording", e);
}
throw new JfrStreamingException("Failed to start recording");
}
private Object[] formOptions(RecordingOptions recordingOptions, RecordingConfiguration recordingConfiguration) throws JfrStreamingException {
List<String> options = recordingOptions.getRecordingOptions()
.entrySet()
.stream()
.filter(kv -> !kv.getKey().equals("disk")) // not supported on Java 8
.map(kv -> kv.getKey() + "=" + kv.getValue())
.collect(Collectors.toList());
if (!(recordingConfiguration instanceof RecordingConfiguration.PredefinedConfiguration)) {
throw new JfrStreamingException("Java 8 currently only supports predefined configurations (default/profile)");
}
List<String> settings = Collections.singletonList(
"settings=" + recordingConfiguration.getConfiguration()
);
List<String> params = new ArrayList<>();
params.addAll(settings);
params.addAll(options);
return mkParamsArray(params);
}
/**
* Stop a recording. This method is called from the {@link Recording#stop()} method.
*
* @param id The id of the recording.
* @throws JfrStreamingException Wraps an {@code javax.management.InstanceNotFoundException},
* a {@code javax.management.MBeanException} or a {@code javax.management.ReflectionException}
* and indicates an issue with the FlightRecorderMXBean in the JVM.
*/
@Override
public void stopRecording(long id) throws JfrStreamingException {
try {
Object[] params = mkParams("name=" + id);
mBeanServerConnection.invoke(objectName, "jfrStop", params, signature);
} catch (InstanceNotFoundException | MBeanException | ReflectionException | IOException e) {
throw new JfrStreamingException("Failed to stop recording", e);
}
}
/**
* Writes recording data to the specified file. The recording must be started, but not necessarily stopped.
* The {@code outputFile} argument is relevant to the machine where the JVM is running.
*
* @param id The id of the recording.
* @param outputFile the system-dependent file name where data is written, not {@code null}
* @throws JfrStreamingException Wraps a {@code javax.management.JMException}.
*/
@Override
public void dumpRecording(long id, String outputFile) throws JfrStreamingException {
try {
Object[] params = mkParams(
"filename=" + outputFile,
"recording=" + id,
"compress=true"
);
mBeanServerConnection.invoke(objectName, "jfrDump", params, signature);
} catch (InstanceNotFoundException | MBeanException | ReflectionException | IOException e) {
throw new JfrStreamingException("Failed to dump recording", e);
}
}
/**
* Not supported on Java 8
*
* @param id The id of the recording being cloned.
* @param stop Whether to stop the cloned recording.
* @return id of the recording
*/
@Override
public long cloneRecording(long id, boolean stop) {
throw new UnsupportedOperationException("Clone not supported on Java 8");
}
/**
* Not supported on Java 8
*/
@Override
public InputStream getStream(long id, Instant startTime, Instant endTime, long blockSize) {
throw new UnsupportedOperationException("getStream not supported on Java 8");
}
/**
* Not supported on Java 8
*/
@Override
public void closeRecording(long id) {
throw new UnsupportedOperationException("closeRecording not supported on Java 8");
}
// visible for testing
static void assertCommercialFeaturesUnlocked(MBeanServerConnection mBeanServerConnection, ObjectName objectName) throws JfrStreamingException {
try {
Object unlockedMessage = mBeanServerConnection.invoke(objectName, "vmCheckCommercialFeatures", null, null);
if (unlockedMessage instanceof String) {
boolean unlocked = ((String) unlockedMessage).contains("unlocked");
if (!unlocked) {
throw new JfrStreamingException("Unlocking commercial features may be required. This must be explicitly enabled by adding -XX:+UnlockCommercialFeatures");
}
}
} catch (InstanceNotFoundException | MBeanException | ReflectionException | IOException e) {
throw new JfrStreamingException("Unable to determine if commercial features are unlocked", e);
}
}
private static Object[] mkParamsArray(List<String> args) {
return new Object[]{args.toArray(new String[0])};
}
private static Object[] mkParams(String... args) {
return new Object[]{args};
}
}

Просмотреть файл

@ -0,0 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* This package provides API for controlling Java Flight Recordings (JFR)
* through the DiagnosticCommand MBean. The code in this package is meant to
* provide a fallback for starting, stopping, and dumping a Java Flight Recording if
* {@link com.microsoft.jfr.FlightRecorderConnection#connect(javax.management.MBeanServerConnection)}
* throws an {@code InstanceNotFoundException} on a Java 8 JVM.
*/
package com.microsoft.jfr.dcmd;

Просмотреть файл

@ -4,7 +4,7 @@
* This package provides API for controlling Java Flight Recordings (JFR) through Java Management Extensions (JMX).
*
* JDK 9 introduced the {@code jdk.jfr} API which is not available in JDK 8. The
* {@code jdk.management.jfr.FlightRecorderMXBean} is available in Java 8 and higher.
* {@code jdk.management.jfr.FlightRecorderMXBean} is available in OpenJDK 8u262 and higher.
* By relying on JMX and the {@code jdk.management.jfr.FlightRecorderMXBean},
* the {@code com.microsoft.jfr} package provides access to JFR on local or remote JVMs.
*/

Просмотреть файл

@ -0,0 +1,71 @@
package com.microsoft.jfr.dcmd;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import com.microsoft.jfr.JfrStreamingException;
import com.microsoft.jfr.RecordingConfiguration;
import com.microsoft.jfr.RecordingOptions;
import org.junit.Test;
import org.testng.Assert;
public class FlightRecorderDiagnosticCommandConnectionTest {
public MBeanServerConnection mockMbeanServer(ObjectName objectName, String vmCheckCommercialFeaturesResponse) throws Exception {
MBeanServerConnection mBeanServerConnection = mock(MBeanServerConnection.class);
when(mBeanServerConnection.invoke(objectName, "vmCheckCommercialFeatures", null, null)).thenReturn(vmCheckCommercialFeaturesResponse);
return mBeanServerConnection;
}
@Test
public void assertCommercialFeaturesUnlocked() throws Exception {
ObjectName objectName = mock(ObjectName.class);
MBeanServerConnection mBeanServerConnection = mockMbeanServer(objectName, "unlocked");
FlightRecorderDiagnosticCommandConnection.assertCommercialFeaturesUnlocked(mBeanServerConnection, objectName);
}
@Test(expected = JfrStreamingException.class)
public void assertCommercialFeaturesLockedThrows() throws Exception {
ObjectName objectName = mock(ObjectName.class);
MBeanServerConnection mBeanServerConnection = mockMbeanServer(objectName, "locked");
FlightRecorderDiagnosticCommandConnection.assertCommercialFeaturesUnlocked(mBeanServerConnection, objectName);
}
private FlightRecorderDiagnosticCommandConnection createconnection() throws Exception {
ObjectName objectName = mock(ObjectName.class);
MBeanServerConnection mBeanServerConnection = mockMbeanServer(objectName, "locked");
return new FlightRecorderDiagnosticCommandConnection(mBeanServerConnection, objectName);
}
@Test(expected = UnsupportedOperationException.class)
public void closeRecording() throws Exception {
createconnection().closeRecording(1);
}
@Test(expected = UnsupportedOperationException.class)
public void testGetStream() throws Exception {
createconnection().getStream(1L, null, null, 0L);
}
@Test(expected = UnsupportedOperationException.class)
public void testCloneRecording() throws Exception {
createconnection().cloneRecording(1, false);
}
@Test
public void startRecordingParsesIdCorrectly() throws Exception {
ObjectName objectName = mock(ObjectName.class);
MBeanServerConnection mBeanServerConnection = mockMbeanServer(objectName, "unlocked");
when(mBeanServerConnection.invoke(any(ObjectName.class), anyString(), any(Object[].class),
any(String[].class))).thenReturn("Started recording 99. ");
FlightRecorderDiagnosticCommandConnection connection = new FlightRecorderDiagnosticCommandConnection(mBeanServerConnection, objectName);
long id = connection.startRecording(new RecordingOptions.Builder().build(), RecordingConfiguration.PROFILE_CONFIGURATION);
Assert.assertEquals(id, 99);
}
}