Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(management): avoid self-referencing JMX operations #280

Merged
merged 13 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 33 additions & 3 deletions src/main/java/io/cryostat/agent/FlightRecorderHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@
*/
package io.cryostat.agent;

import java.io.IOException;
import java.io.StringReader;
import java.text.ParseException;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
Expand All @@ -32,7 +36,25 @@ public class FlightRecorderHelper {

private final Logger log = LoggerFactory.getLogger(getClass());

public Optional<TemplatedRecording> createRecording(String templateNameOrLabel) {
public Optional<Recording> createSnapshot() {
Recording snapshot = FlightRecorder.getFlightRecorder().takeSnapshot();
if (snapshot.getSize() == 0) {
log.warn("No active recordings");
snapshot.close();
return Optional.empty();
}
return Optional.of(snapshot);
}

public Recording createRecordingWithCustomTemplate(String template)
throws IOException, ParseException {
Recording recording = new Recording(Configuration.create(new StringReader(template)));
recording.setToDisk(true);
return recording;
}

public Optional<TemplatedRecording> createRecordingWithPredefinedTemplate(
String templateNameOrLabel) {
Optional<Configuration> opt = getTemplate(templateNameOrLabel);
if (opt.isEmpty()) {
log.error(
Expand All @@ -58,16 +80,24 @@ public boolean isValidTemplate(String nameOrLabel) {
return getTemplate(nameOrLabel).isPresent();
}

public List<RecordingInfo> getRecordings() {
public List<Recording> getRecordings() {
return getRecordings(r -> true);
}

public List<Recording> getRecordings(Predicate<Recording> predicate) {
if (!FlightRecorder.isAvailable()) {
log.error("FlightRecorder is unavailable");
return List.of();
}
return FlightRecorder.getFlightRecorder().getRecordings().stream()
.map(RecordingInfo::new)
.filter(predicate)
.collect(Collectors.toList());
}

public Optional<Recording> getRecording(long id) {
return getRecordings(r -> r.getId() == id).stream().findFirst();
}

@SuppressFBWarnings(value = {"EI_EXPOSE_REP", "EI_EXPOSE_REP2"})
public static class TemplatedRecording {
private final Configuration configuration;
Expand Down
79 changes: 5 additions & 74 deletions src/main/java/io/cryostat/agent/MainModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@
*/
package io.cryostat.agent;

import java.io.IOException;
import java.net.URI;
import java.nio.file.Path;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
Expand All @@ -38,12 +36,8 @@
import io.cryostat.agent.remote.RemoteContext;
import io.cryostat.agent.remote.RemoteModule;
import io.cryostat.agent.triggers.TriggerModule;
import io.cryostat.core.net.JFRConnection;
import io.cryostat.core.net.JFRConnectionToolkit;
import io.cryostat.core.sys.Environment;
import io.cryostat.core.sys.FileSystem;
import io.cryostat.core.templates.LocalStorageTemplateService;
import io.cryostat.core.tui.ClientWriter;
import io.cryostat.core.JvmIdentifier;
import io.cryostat.core.net.IDException;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
Expand Down Expand Up @@ -71,7 +65,6 @@ public abstract class MainModule {
// one for outbound HTTP requests, one for incoming HTTP requests, and one as a general worker
private static final int NUM_WORKER_THREADS = 3;
private static final String JVM_ID = "JVM_ID";
private static final String TEMPLATES_PATH = "TEMPLATES_PATH";

@Provides
@Singleton
Expand Down Expand Up @@ -242,76 +235,14 @@ public static FlightRecorderHelper provideFlightRecorderHelper() {
return new FlightRecorderHelper();
}

@Provides
@Singleton
public static FileSystem provideFileSystem() {
return new FileSystem();
}

@Provides
@Singleton
@Named(TEMPLATES_PATH)
public static Path provideTemplatesTmpPath(FileSystem fs) {
try {
return fs.createTempDirectory(null);
} catch (IOException e) {
throw new RuntimeException(e);
}
}

@Provides
@Singleton
public static Environment provideEnvironment(@Named(TEMPLATES_PATH) Path templatesTmp) {
return new Environment() {
@Override
public String getEnv(String key) {
if (LocalStorageTemplateService.TEMPLATE_PATH.equals(key)) {
andrewazores marked this conversation as resolved.
Show resolved Hide resolved
return templatesTmp.toString();
}
return super.getEnv(key);
}
};
}

@Provides
@Singleton
public static ClientWriter provideClientWriter() {
Logger log = LoggerFactory.getLogger(JFRConnectionToolkit.class);
return new ClientWriter() {
@Override
public void print(String msg) {
log.info(msg);
}
};
}

@Provides
@Singleton
public static JFRConnectionToolkit provideJfrConnectionToolkit(
ClientWriter cw, FileSystem fs, Environment env) {
return new JFRConnectionToolkit(cw, fs, env);
}

@Provides
@Singleton
@Named(JVM_ID)
public static String provideJvmId(JFRConnectionToolkit tk) {
Logger log = LoggerFactory.getLogger(JFRConnectionToolkit.class);
public static String provideJvmId() {
try {
try (JFRConnection connection = tk.connect(tk.createServiceURL("localhost", 0))) {
String id = connection.getJvmId();
log.info("Computed self JVM ID: {}", id);
return id;
}
} catch (Exception e) {
return JvmIdentifier.getLocal().getHash();
} catch (IDException e) {
throw new RuntimeException(e);
}
}

@Provides
@Singleton
public static LocalStorageTemplateService provideLocalStorageTemplateService(
FileSystem fs, Environment env) {
return new LocalStorageTemplateService(fs, env);
}
}
2 changes: 1 addition & 1 deletion src/main/java/io/cryostat/agent/harvest/Harvester.java
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ private void startRecording(boolean restart) {
return;
}
flightRecorderHelper
.createRecording(template)
.createRecordingWithPredefinedTemplate(template)
.ifPresent(
recording -> {
recording
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,14 @@

import java.io.IOException;
import java.io.OutputStream;
import java.lang.management.ManagementFactory;
import java.util.List;
import java.util.stream.Collectors;

import javax.inject.Inject;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.net.httpserver.HttpExchange;
import jdk.management.jfr.ConfigurationInfo;
import jdk.management.jfr.FlightRecorderMXBean;
import jdk.jfr.Configuration;
import org.apache.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -53,11 +51,9 @@ public void handle(HttpExchange exchange) throws IOException {
switch (mtd) {
case "GET":
try {
FlightRecorderMXBean bean =
ManagementFactory.getPlatformMXBean(FlightRecorderMXBean.class);
List<String> xmlTexts =
bean.getConfigurations().stream()
.map(ConfigurationInfo::getContents)
Configuration.getConfigurations().stream()
.map(Configuration::getContents)
.collect(Collectors.toList());
exchange.sendResponseHeaders(HttpStatus.SC_OK, BODY_LENGTH_UNKNOWN);
try (OutputStream response = exchange.getResponseBody()) {
Expand Down
Loading
Loading