Skip to content

Commit

Permalink
feat(diagnostics): Add API handler for invoking GC (#492)
Browse files Browse the repository at this point in the history
Co-authored-by: Andrew Azores <me@andrewazor.es>
  • Loading branch information
Josh-Matsuoka and andrewazores authored Oct 1, 2024
1 parent 552b8f7 commit f35e374
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 2 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,18 @@ stopped or the host JVM shuts down.

## Configuration

### Logging

`cryostat-agent` uses [SLF4J](https://www.slf4j.org/) for its logging. Currently it ships with `slf4j-simple` as the
log implementation, but this should change in the future to hook in to any existing SLF4J providers found on the
classpath at runtime, or fallback to `slf4j-simple` if nothing is found.

The Agent's log levels can be controlled by setting a JVM system property on the target JVM:
`-Dio.cryostat.agent.shaded.org.slf4j.simpleLogger.defaultLogLevel=trace`, or replace `trace` with whatever level you
prefer. This can also be passed as a dynamic attachment argument when starting the Agent after the target JVM.

### Agent Properties

`cryostat-agent` uses [smallrye-config](https://github.com/smallrye/smallrye-config) for configuration.
Below is a list of configuration properties that can be used to influence how `cryostat-agent` runs
and how it advertises itself to a Cryostat server instance. Properties that require configuration are indicated with a checked box.
Expand Down
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
<com.github.spotbugs.plugin.version>4.8.6.4</com.github.spotbugs.plugin.version>
<org.junit.jupiter.version>5.11.1</org.junit.jupiter.version>
<org.hamcrest.version>3.0</org.hamcrest.version>
<org.mockito.version>5.2.0</org.mockito.version>
<org.mockito.version>5.14.1</org.mockito.version>
<org.jacoco.maven.plugin.version>0.8.12</org.jacoco.maven.plugin.version>
<com.diffplug.spotless.maven.plugin.version>2.43.0</com.diffplug.spotless.maven.plugin.version>
<com.google.googlejavaformat.version>1.17.0</com.google.googlejavaformat.version>
Expand Down Expand Up @@ -256,7 +256,7 @@
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<artifactId>mockito-core</artifactId>
<version>${org.mockito.version}</version>
<scope>test</scope>
</dependency>
Expand Down
117 changes: 117 additions & 0 deletions src/main/java/io/cryostat/agent/remote/InvokeContext.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* Copyright The Cryostat Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cryostat.agent.remote;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.management.ManagementFactory;
import java.util.Objects;

import javax.inject.Inject;
import javax.management.MBeanServer;
import javax.management.ObjectName;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.net.httpserver.HttpExchange;
import org.apache.http.HttpStatus;
import org.eclipse.microprofile.config.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class InvokeContext extends MutatingRemoteContext {

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

@Inject
InvokeContext(ObjectMapper mapper, Config config) {
super(config);
this.mapper = mapper;
}

@Override
public String path() {
return "/mbean-invoke/";
}

@Override
public void handle(HttpExchange exchange) throws IOException {
try {
String mtd = exchange.getRequestMethod();
switch (mtd) {
case "POST":
try (InputStream body = exchange.getRequestBody()) {
MBeanInvocationRequest req =
mapper.readValue(body, MBeanInvocationRequest.class);
if (!req.isValid()) {
exchange.sendResponseHeaders(
HttpStatus.SC_BAD_REQUEST, BODY_LENGTH_NONE);
}
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
Object response =
server.invoke(
ObjectName.getInstance(req.beanName),
req.operation,
req.parameters,
req.signature);
if (Objects.nonNull(response)) {
exchange.sendResponseHeaders(HttpStatus.SC_OK, BODY_LENGTH_UNKNOWN);
try (OutputStream responseStream = exchange.getResponseBody()) {
mapper.writeValue(responseStream, response);
}
} else {
exchange.sendResponseHeaders(HttpStatus.SC_ACCEPTED, BODY_LENGTH_NONE);
}
} catch (Exception e) {
log.error("mbean serialization failure", e);
exchange.sendResponseHeaders(HttpStatus.SC_BAD_GATEWAY, BODY_LENGTH_NONE);
}
break;
default:
log.warn("Unknown request method {}", mtd);
exchange.sendResponseHeaders(
HttpStatus.SC_METHOD_NOT_ALLOWED, BODY_LENGTH_NONE);
break;
}
} finally {
exchange.close();
}
}

static class MBeanInvocationRequest<T> {

public String beanName;
public String operation;
public Object[] parameters;
public String[] signature;

public boolean isValid() {
if (this.beanName.equals(ManagementFactory.MEMORY_MXBEAN_NAME)) {
return true;
}
return false;
}

public String getBeanName() {
return beanName;
}

public void setBeanName(String beanName) {
this.beanName = beanName;
}
}
}
4 changes: 4 additions & 0 deletions src/main/java/io/cryostat/agent/remote/RemoteModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
@Module
public abstract class RemoteModule {

@Binds
@IntoSet
abstract RemoteContext bindInvokeContext(InvokeContext ctx);

@Binds
@IntoSet
abstract RemoteContext bindMBeanContext(MBeanContext ctx);
Expand Down

0 comments on commit f35e374

Please sign in to comment.