Skip to content

Commit f937f4f

Browse files
LeMystnyg
andcommitted
Avoid usage of jdk.jconsole module in Java 9+
Copied from PR jiaqi#113 Co-Authored-By: nyg <[email protected]>
1 parent 741dfd5 commit f937f4f

File tree

8 files changed

+246
-6
lines changed

8 files changed

+246
-6
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package org.cyclopsgroup.jmxterm.jdk9;
2+
3+
import java.io.IOException;
4+
import org.apache.commons.lang3.Validate;
5+
import org.cyclopsgroup.jmxterm.JavaProcess;
6+
import org.cyclopsgroup.jmxterm.utils.WeakCastUtils;
7+
8+
/**
9+
* JDK9 specific implementation of {@link JavaProcess}
10+
*
11+
* @author <a href="https://github.com/nyg">nyg</a>
12+
*/
13+
public class Jdk9JavaProcess implements JavaProcess {
14+
15+
private final StaticVirtualMachine staticVirtualMachine;
16+
private final VirtualMachineDescriptor vmd;
17+
private final String address;
18+
19+
/**
20+
* @param staticVm Static VirtualMachine proxy
21+
* @param vmd Local VM
22+
* @param address Connector address, if any
23+
*/
24+
Jdk9JavaProcess(StaticVirtualMachine staticVm, VirtualMachineDescriptor vmd, String address) {
25+
Validate.notNull(vmd, "StaticVirtualMachine can't be NULL");
26+
Validate.notNull(vmd, "VirtualMachineDescriptor can't be NULL");
27+
this.staticVirtualMachine = staticVm;
28+
this.vmd = vmd;
29+
this.address = address;
30+
}
31+
32+
@Override
33+
public String getDisplayName() {
34+
return vmd.displayName();
35+
}
36+
37+
@Override
38+
public int getProcessId() {
39+
return Integer.parseInt(vmd.id());
40+
}
41+
42+
@Override
43+
public boolean isManageable() {
44+
return address != null;
45+
}
46+
47+
@Override
48+
public void startManagementAgent() throws IOException {
49+
Object vm = staticVirtualMachine.attach(vmd.id());
50+
try {
51+
Class<?> originalVirtualMachine = Class.forName(VirtualMachine.ORIGINAL_CLASS_NAME);
52+
VirtualMachine vmProxy = WeakCastUtils.cast(originalVirtualMachine, VirtualMachine.class);
53+
vmProxy.startLocalManagementAgent();
54+
} catch (ClassNotFoundException | SecurityException | NoSuchMethodException e) {
55+
throw new RuntimeException("Can't cast " + vm + " to VirtualMachineDescriptor", e);
56+
}
57+
}
58+
59+
@Override
60+
public String toUrl() {
61+
return address;
62+
}
63+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package org.cyclopsgroup.jmxterm.jdk9;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import java.util.Properties;
6+
import org.apache.commons.lang3.Validate;
7+
import org.cyclopsgroup.jmxterm.JavaProcess;
8+
import org.cyclopsgroup.jmxterm.JavaProcessManager;
9+
import org.cyclopsgroup.jmxterm.utils.WeakCastUtils;
10+
11+
/**
12+
* JDK9 specific java process manager
13+
*
14+
* @author <a href="https://github.com/nyg">nyg</a>
15+
*/
16+
public class Jdk9JavaProcessManager extends JavaProcessManager {
17+
private final StaticVirtualMachine staticVirtualMachine;
18+
private final Class<?> originalVirtualMachine;
19+
20+
public Jdk9JavaProcessManager(ClassLoader classLoader)
21+
throws SecurityException, NoSuchMethodException, ClassNotFoundException {
22+
Validate.notNull(classLoader, "ClassLoader can't be NULL");
23+
originalVirtualMachine = classLoader.loadClass(VirtualMachine.ORIGINAL_CLASS_NAME);
24+
staticVirtualMachine =
25+
WeakCastUtils.staticCast(originalVirtualMachine, StaticVirtualMachine.class);
26+
}
27+
28+
@Override
29+
public JavaProcess get(int pid) {
30+
return list().stream().filter(process -> process.getProcessId() == pid).findAny().orElse(null);
31+
}
32+
33+
@Override
34+
public List<JavaProcess> list() {
35+
List<Object> vmDescriptors = staticVirtualMachine.list();
36+
List<JavaProcess> result = new ArrayList<>(vmDescriptors.size());
37+
38+
for (Object vmd : vmDescriptors) {
39+
VirtualMachineDescriptor vmdProxy = null;
40+
VirtualMachine vmProxy = null;
41+
42+
try {
43+
vmdProxy = WeakCastUtils.cast(vmd, VirtualMachineDescriptor.class);
44+
Object vm = staticVirtualMachine.attach(vmdProxy.id());
45+
vmProxy = WeakCastUtils.cast(originalVirtualMachine, vm, VirtualMachine.class);
46+
47+
Properties agentProps = vmProxy.getAgentProperties();
48+
String address = (String) agentProps.get(VirtualMachine.LOCAL_CONNECTOR_ADDRESS_PROP);
49+
result.add(new Jdk9JavaProcess(staticVirtualMachine, vmdProxy, address));
50+
} catch (SecurityException | NoSuchMethodException e) {
51+
throw new RuntimeException("Error casting object", e);
52+
} catch (Exception e) {
53+
// could not attach or some other exception
54+
result.add(new Jdk9JavaProcess(staticVirtualMachine, vmdProxy, null));
55+
} finally {
56+
if (vmProxy != null) {
57+
vmProxy.detach();
58+
}
59+
}
60+
}
61+
return result;
62+
}
63+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.cyclopsgroup.jmxterm.jdk9;
2+
3+
import java.util.List;
4+
5+
/**
6+
* Static interface of com.sun.tools.attach.VirtualMachine
7+
*
8+
* @author <a href="https://github.com/nyg">nyg</a>
9+
*/
10+
public interface StaticVirtualMachine {
11+
/** @return List of all virtual machines running on local */
12+
List<Object> list();
13+
14+
/** Attaches to a Java virtual machine. */
15+
Object attach(String id);
16+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package org.cyclopsgroup.jmxterm.jdk9;
2+
3+
import java.util.Properties;
4+
5+
/**
6+
* Reflect class com.sun.tools.attach.VirtualMachine
7+
*
8+
* @author <a href="https://github.com/nyg">nyg</a>
9+
*/
10+
public interface VirtualMachine {
11+
/** Name of original class this interface reflects */
12+
String ORIGINAL_CLASS_NAME = "com.sun.tools.attach.VirtualMachine";
13+
14+
/** Property for the local connector address */
15+
String LOCAL_CONNECTOR_ADDRESS_PROP = "com.sun.management.jmxremote.localConnectorAddress";
16+
17+
/** Detach from the virtual machine. */
18+
void detach();
19+
20+
/** @return The current agent properties in the target virtual machine. */
21+
Properties getAgentProperties();
22+
23+
/** Starts the local JMX management agent in the target virtual machine. */
24+
void startLocalManagementAgent();
25+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.cyclopsgroup.jmxterm.jdk9;
2+
3+
/**
4+
* Reflect class com.sun.tools.attach.VirtualMachineDescriptor
5+
*
6+
* @author <a href="https://github.com/nyg">nyg</a>
7+
*/
8+
public interface VirtualMachineDescriptor {
9+
/** Name of original class this interface reflects */
10+
String ORIGINAL_CLASS_NAME = "com.sun.tools.attach.VirtualMachineDescriptor";
11+
12+
/** @return The display name component of this descriptor */
13+
String displayName();
14+
15+
/** @return The identifier component of this descriptor */
16+
String id();
17+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/**
2+
* Classes to implement {@link org.cyclopsgroup.jmxterm.JavaProcessManager} in JDK9 specific way
3+
*
4+
* @author <a href="https://github.com/nyg">nyg</a>
5+
*/
6+
package org.cyclopsgroup.jmxterm.jdk9;

src/main/java/org/cyclopsgroup/jmxterm/utils/WeakCastUtils.java

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,19 @@ public final class WeakCastUtils {
1919
/**
2020
* Cast object into multiple interfaces
2121
*
22+
* @param original The interface of the from instance
2223
* @param from Object to cast
2324
* @param interfaces Interfaces to cast to
2425
* @param classLoader ClassLoader to load methods for invocation
2526
* @return Result that implements given interfaces
2627
* @throws SecurityException Allows exception related to security.
2728
* @throws NoSuchMethodException Allows exception due to wrong method.
2829
*/
29-
public static Object cast(final Object from, final Class<?>[] interfaces, ClassLoader classLoader)
30+
public static Object cast(
31+
final Class<?> original,
32+
final Object from,
33+
final Class<?>[] interfaces,
34+
ClassLoader classLoader)
3035
throws SecurityException, NoSuchMethodException {
3136
Validate.notNull(from, "Invocation target can't be NULL");
3237
Validate.notNull(interfaces, "Interfaces can't be NULL");
@@ -35,8 +40,7 @@ public static Object cast(final Object from, final Class<?>[] interfaces, ClassL
3540
for (Class<?> interfase : interfaces) {
3641
Validate.isTrue(interfase.isInterface(), interfase + " is not an interface");
3742
for (Method fromMethod : interfase.getMethods()) {
38-
Method toMethod =
39-
from.getClass().getMethod(fromMethod.getName(), fromMethod.getParameterTypes());
43+
Method toMethod = original.getMethod(fromMethod.getName(), fromMethod.getParameterTypes());
4044
methodMap.put(fromMethod, toMethod);
4145
}
4246
}
@@ -74,7 +78,23 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
7478
*/
7579
public static <T> T cast(Object from, Class<T> interfase)
7680
throws SecurityException, NoSuchMethodException {
77-
return cast(from, interfase, interfase.getClassLoader());
81+
return cast(from.getClass(), from, interfase, interfase.getClassLoader());
82+
}
83+
84+
/**
85+
* Cast object to one given interface using ClassLoader of interface
86+
*
87+
* @param original The interface of the from instance
88+
* @param <T> Type of interface
89+
* @param from Object to cast
90+
* @param interfase Interface to cast to
91+
* @return Result that implements interface
92+
* @throws SecurityException Allows exception related to security.
93+
* @throws NoSuchMethodException Allows exception due to wrong method.
94+
*/
95+
public static <T> T cast(Class<?> original, Object from, Class<T> interfase)
96+
throws SecurityException, NoSuchMethodException {
97+
return cast(original, from, interfase, interfase.getClassLoader());
7898
}
7999

80100
/**
@@ -89,10 +109,11 @@ public static <T> T cast(Object from, Class<T> interfase)
89109
* @throws NoSuchMethodException Allows exception due to wrong method.
90110
*/
91111
@SuppressWarnings("unchecked")
92-
public static <T> T cast(Object from, Class<T> interfase, ClassLoader classLoader)
112+
public static <T> T cast(
113+
Class<?> original, Object from, Class<T> interfase, ClassLoader classLoader)
93114
throws SecurityException, NoSuchMethodException {
94115
Validate.notNull(interfase, "Interface can't be NULL");
95-
return (T) cast(from, new Class<?>[] {interfase}, classLoader);
116+
return (T) cast(original, from, new Class<?>[] {interfase}, classLoader);
96117
}
97118

98119
/**
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package org.cyclopsgroup.jmxterm.jdk9;
2+
3+
import static org.junit.Assert.assertFalse;
4+
5+
import java.util.List;
6+
import org.apache.commons.lang3.JavaVersion;
7+
import org.apache.commons.lang3.SystemUtils;
8+
import org.cyclopsgroup.jmxterm.JavaProcess;
9+
import org.cyclopsgroup.jmxterm.pm.JConsoleClassLoaderFactory;
10+
import org.junit.Test;
11+
12+
/**
13+
* Test case of {@link Jdk9JavaProcessManager}
14+
*
15+
* @author <a href="https://github.com/nyg">nyg</a>
16+
*/
17+
public class Jdk9JavaProcessManagerTest {
18+
19+
@Test
20+
public void testConstruction() throws Exception {
21+
if (!SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_9)) {
22+
return;
23+
}
24+
Jdk9JavaProcessManager jpm =
25+
new Jdk9JavaProcessManager(JConsoleClassLoaderFactory.getClassLoader());
26+
List<JavaProcess> ps = jpm.list();
27+
assertFalse(ps.isEmpty());
28+
}
29+
}

0 commit comments

Comments
 (0)