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(diagnostics): Add API handler for invoking GC #492

Merged
merged 10 commits into from
Oct 1, 2024
118 changes: 118 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,118 @@
/*
* 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_CREATED, BODY_LENGTH_NONE);
Josh-Matsuoka marked this conversation as resolved.
Show resolved Hide resolved
}
} 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;

// Support GC and Thread operations for now
andrewazores marked this conversation as resolved.
Show resolved Hide resolved
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
Loading