Skip to content

Commit

Permalink
Avoid usage of jdk.jconsole module in Java 9+
Browse files Browse the repository at this point in the history
Copied from PR jiaqi#113

Co-Authored-By: nyg <[email protected]>
  • Loading branch information
LeMyst and nyg committed Jan 24, 2025
1 parent 8546b99 commit cef942f
Show file tree
Hide file tree
Showing 8 changed files with 246 additions and 6 deletions.
63 changes: 63 additions & 0 deletions src/main/java/org/cyclopsgroup/jmxterm/jdk9/Jdk9JavaProcess.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package org.cyclopsgroup.jmxterm.jdk9;

import java.io.IOException;
import org.apache.commons.lang3.Validate;
import org.cyclopsgroup.jmxterm.JavaProcess;
import org.cyclopsgroup.jmxterm.utils.WeakCastUtils;

/**
* JDK9 specific implementation of {@link JavaProcess}
*
* @author <a href="https://github.com/nyg">nyg</a>
*/
public class Jdk9JavaProcess implements JavaProcess {

private final StaticVirtualMachine staticVirtualMachine;
private final VirtualMachineDescriptor vmd;
private final String address;

/**
* @param staticVm Static VirtualMachine proxy
* @param vmd Local VM
* @param address Connector address, if any
*/
Jdk9JavaProcess(StaticVirtualMachine staticVm, VirtualMachineDescriptor vmd, String address) {
Validate.notNull(vmd, "StaticVirtualMachine can't be NULL");
Validate.notNull(vmd, "VirtualMachineDescriptor can't be NULL");
this.staticVirtualMachine = staticVm;
this.vmd = vmd;
this.address = address;
}

@Override
public String getDisplayName() {
return vmd.displayName();
}

@Override
public int getProcessId() {
return Integer.parseInt(vmd.id());
}

@Override
public boolean isManageable() {
return address != null;
}

@Override
public void startManagementAgent() throws IOException {
Object vm = staticVirtualMachine.attach(vmd.id());
try {
Class<?> originalVirtualMachine = Class.forName(VirtualMachine.ORIGINAL_CLASS_NAME);
VirtualMachine vmProxy = WeakCastUtils.cast(originalVirtualMachine, VirtualMachine.class);
vmProxy.startLocalManagementAgent();
} catch (ClassNotFoundException | SecurityException | NoSuchMethodException e) {
throw new RuntimeException("Can't cast " + vm + " to VirtualMachineDescriptor", e);
}
}

@Override
public String toUrl() {
return address;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package org.cyclopsgroup.jmxterm.jdk9;

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.apache.commons.lang3.Validate;
import org.cyclopsgroup.jmxterm.JavaProcess;
import org.cyclopsgroup.jmxterm.JavaProcessManager;
import org.cyclopsgroup.jmxterm.utils.WeakCastUtils;

/**
* JDK9 specific java process manager
*
* @author <a href="https://github.com/nyg">nyg</a>
*/
public class Jdk9JavaProcessManager extends JavaProcessManager {
private final StaticVirtualMachine staticVirtualMachine;
private final Class<?> originalVirtualMachine;

public Jdk9JavaProcessManager(ClassLoader classLoader)
throws SecurityException, NoSuchMethodException, ClassNotFoundException {
Validate.notNull(classLoader, "ClassLoader can't be NULL");
originalVirtualMachine = classLoader.loadClass(VirtualMachine.ORIGINAL_CLASS_NAME);
staticVirtualMachine =
WeakCastUtils.staticCast(originalVirtualMachine, StaticVirtualMachine.class);
}

@Override
public JavaProcess get(int pid) {
return list().stream().filter(process -> process.getProcessId() == pid).findAny().orElse(null);
}

@Override
public List<JavaProcess> list() {
List<Object> vmDescriptors = staticVirtualMachine.list();
List<JavaProcess> result = new ArrayList<>(vmDescriptors.size());

for (Object vmd : vmDescriptors) {
VirtualMachineDescriptor vmdProxy = null;
VirtualMachine vmProxy = null;

try {
vmdProxy = WeakCastUtils.cast(vmd, VirtualMachineDescriptor.class);
Object vm = staticVirtualMachine.attach(vmdProxy.id());
vmProxy = WeakCastUtils.cast(originalVirtualMachine, vm, VirtualMachine.class);

Properties agentProps = vmProxy.getAgentProperties();
String address = (String) agentProps.get(VirtualMachine.LOCAL_CONNECTOR_ADDRESS_PROP);
result.add(new Jdk9JavaProcess(staticVirtualMachine, vmdProxy, address));
} catch (SecurityException | NoSuchMethodException e) {
throw new RuntimeException("Error casting object", e);
} catch (Exception e) {
// could not attach or some other exception
result.add(new Jdk9JavaProcess(staticVirtualMachine, vmdProxy, null));
} finally {
if (vmProxy != null) {
vmProxy.detach();
}
}
}
return result;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.cyclopsgroup.jmxterm.jdk9;

import java.util.List;

/**
* Static interface of com.sun.tools.attach.VirtualMachine
*
* @author <a href="https://github.com/nyg">nyg</a>
*/
public interface StaticVirtualMachine {
/** @return List of all virtual machines running on local */
List<Object> list();

/** Attaches to a Java virtual machine. */
Object attach(String id);
}
25 changes: 25 additions & 0 deletions src/main/java/org/cyclopsgroup/jmxterm/jdk9/VirtualMachine.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.cyclopsgroup.jmxterm.jdk9;

import java.util.Properties;

/**
* Reflect class com.sun.tools.attach.VirtualMachine
*
* @author <a href="https://github.com/nyg">nyg</a>
*/
public interface VirtualMachine {
/** Name of original class this interface reflects */
String ORIGINAL_CLASS_NAME = "com.sun.tools.attach.VirtualMachine";

/** Property for the local connector address */
String LOCAL_CONNECTOR_ADDRESS_PROP = "com.sun.management.jmxremote.localConnectorAddress";

/** Detach from the virtual machine. */
void detach();

/** @return The current agent properties in the target virtual machine. */
Properties getAgentProperties();

/** Starts the local JMX management agent in the target virtual machine. */
void startLocalManagementAgent();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.cyclopsgroup.jmxterm.jdk9;

/**
* Reflect class com.sun.tools.attach.VirtualMachineDescriptor
*
* @author <a href="https://github.com/nyg">nyg</a>
*/
public interface VirtualMachineDescriptor {
/** Name of original class this interface reflects */
String ORIGINAL_CLASS_NAME = "com.sun.tools.attach.VirtualMachineDescriptor";

/** @return The display name component of this descriptor */
String displayName();

/** @return The identifier component of this descriptor */
String id();
}
6 changes: 6 additions & 0 deletions src/main/java/org/cyclopsgroup/jmxterm/jdk9/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* Classes to implement {@link org.cyclopsgroup.jmxterm.JavaProcessManager} in JDK9 specific way
*
* @author <a href="https://github.com/nyg">nyg</a>
*/
package org.cyclopsgroup.jmxterm.jdk9;
33 changes: 27 additions & 6 deletions src/main/java/org/cyclopsgroup/jmxterm/utils/WeakCastUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,19 @@ public final class WeakCastUtils {
/**
* Cast object into multiple interfaces
*
* @param original The interface of the from instance
* @param from Object to cast
* @param interfaces Interfaces to cast to
* @param classLoader ClassLoader to load methods for invocation
* @return Result that implements given interfaces
* @throws SecurityException Allows exception related to security.
* @throws NoSuchMethodException Allows exception due to wrong method.
*/
public static Object cast(final Object from, final Class<?>[] interfaces, ClassLoader classLoader)
public static Object cast(
final Class<?> original,
final Object from,
final Class<?>[] interfaces,
ClassLoader classLoader)
throws SecurityException, NoSuchMethodException {
Validate.notNull(from, "Invocation target can't be NULL");
Validate.notNull(interfaces, "Interfaces can't be NULL");
Expand All @@ -35,8 +40,7 @@ public static Object cast(final Object from, final Class<?>[] interfaces, ClassL
for (Class<?> interfase : interfaces) {
Validate.isTrue(interfase.isInterface(), interfase + " is not an interface");
for (Method fromMethod : interfase.getMethods()) {
Method toMethod =
from.getClass().getMethod(fromMethod.getName(), fromMethod.getParameterTypes());
Method toMethod = original.getMethod(fromMethod.getName(), fromMethod.getParameterTypes());
methodMap.put(fromMethod, toMethod);
}
}
Expand Down Expand Up @@ -74,7 +78,23 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
*/
public static <T> T cast(Object from, Class<T> interfase)
throws SecurityException, NoSuchMethodException {
return cast(from, interfase, interfase.getClassLoader());
return cast(from.getClass(), from, interfase, interfase.getClassLoader());
}

/**
* Cast object to one given interface using ClassLoader of interface
*
* @param original The interface of the from instance
* @param <T> Type of interface
* @param from Object to cast
* @param interfase Interface to cast to
* @return Result that implements interface
* @throws SecurityException Allows exception related to security.
* @throws NoSuchMethodException Allows exception due to wrong method.
*/
public static <T> T cast(Class<?> original, Object from, Class<T> interfase)
throws SecurityException, NoSuchMethodException {
return cast(original, from, interfase, interfase.getClassLoader());
}

/**
Expand All @@ -89,10 +109,11 @@ public static <T> T cast(Object from, Class<T> interfase)
* @throws NoSuchMethodException Allows exception due to wrong method.
*/
@SuppressWarnings("unchecked")
public static <T> T cast(Object from, Class<T> interfase, ClassLoader classLoader)
public static <T> T cast(
Class<?> original, Object from, Class<T> interfase, ClassLoader classLoader)
throws SecurityException, NoSuchMethodException {
Validate.notNull(interfase, "Interface can't be NULL");
return (T) cast(from, new Class<?>[] {interfase}, classLoader);
return (T) cast(original, from, new Class<?>[] {interfase}, classLoader);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.cyclopsgroup.jmxterm.jdk9;

import static org.junit.Assert.assertFalse;

import java.util.List;
import org.apache.commons.lang3.JavaVersion;
import org.apache.commons.lang3.SystemUtils;
import org.cyclopsgroup.jmxterm.JavaProcess;
import org.cyclopsgroup.jmxterm.pm.JConsoleClassLoaderFactory;
import org.junit.Test;

/**
* Test case of {@link Jdk9JavaProcessManager}
*
* @author <a href="https://github.com/nyg">nyg</a>
*/
public class Jdk9JavaProcessManagerTest {

@Test
public void testConstruction() throws Exception {
if (!SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_9)) {
return;
}
Jdk9JavaProcessManager jpm =
new Jdk9JavaProcessManager(JConsoleClassLoaderFactory.getClassLoader());
List<JavaProcess> ps = jpm.list();
assertFalse(ps.isEmpty());
}
}

0 comments on commit cef942f

Please sign in to comment.