/
VirtualMachineHandler.java
188 lines (171 loc) · 7.75 KB
/
VirtualMachineHandler.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
package org.jolokia.jvmagent.client.util;
/*
* Copyright 2009-2013 Roland Huss
*
* 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.
*/
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.regex.Pattern;
/**
* A handler for dealing with <code>VirtualMachine</code> without directly referencing internally
* the class type. All lookup is done via reflection. (Name not changed for compatibility reasons).
*
* @author roland
* @since 12.08.11
*/
class VirtualMachineHandler implements VirtualMachineHandlerOperations {
private final OptionsAndArgs options;
/**
* Constructor with options
*
* @param pOptions options for getting e.g. the process id to attach to
*
*/
VirtualMachineHandler(OptionsAndArgs pOptions) {
options = pOptions;
}
/**
* Lookup and create a {@link com.sun.tools.attach.VirtualMachine} via reflection. First, a direct
* lookup via {@link Class#forName(String)} is done, which will succeed for JVM on OS X, since tools.jar
* is bundled there together with classes.zip. Next, tools.jar is tried to be found (by examine <code>java.home</code>)
* and an own classloader is created for looking up the VirtualMachine.
*
* If lookup fails, a message is printed out (except when '--quiet' is provided)
*
* @return the virtual machine of <code>null</code> if none could be created
*/
@Override
public Object attachVirtualMachine() throws ProcessingException {
if (options.getPid() == null && options.getProcessPattern() == null) {
return null;
}
Class<?> vmClass = lookupVirtualMachineClass();
String pid = null;
try {
Method method = vmClass.getMethod("attach",String.class);
pid = PlatformUtils.getProcessId(this, options);
return method.invoke(null, pid);
} catch (NoSuchMethodException e) {
throw new ProcessingException("Internal: No method 'attach' found on " + vmClass,e,options);
} catch (InvocationTargetException e) {
throw new ProcessingException(getPidErrorMesssage(pid,"InvocationTarget",vmClass),e,options);
} catch (IllegalAccessException e) {
throw new ProcessingException(getPidErrorMesssage(pid, "IllegalAccessException", vmClass),e,options);
} catch (IllegalArgumentException e) {
throw new ProcessingException("Illegal Argument",e,options);
}
}
private String getPidErrorMesssage(String pid, String label, Class<?> vmClass) {
return pid != null ?
String.format("Cannot attach to process-ID %s (%s %s).\nSee --help for possible reasons.",
pid, label, vmClass.getName()) :
String.format("%s %s",label, vmClass.getName());
}
/**
* Detach from the virtual machine
*
* @param pVm the virtual machine to detach from
*/
@Override
public void detachAgent(Object pVm) {
try {
if (pVm != null) {
Class<?> clazz = pVm.getClass();
Method method = clazz.getMethod("detach");
method.setAccessible(true); // on J9 you get IllegalAccessException otherwise.
method.invoke(pVm);
}
} catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
throw new ProcessingException("Error while detaching",e, options);
}
}
/**
* Return a list of all Java processes
* @return list of java processes
* @throws ProcessingException reflection error
*/
@Override
public List<ProcessDescription> listProcesses() {
List<ProcessDescription> ret = new ArrayList<>();
Class<?> vmClass = lookupVirtualMachineClass();
try {
Method method = vmClass.getMethod("list");
List<?> vmDescriptors = (List<?>) method.invoke(null);
for (Object descriptor : vmDescriptors) {
Method idMethod = descriptor.getClass().getMethod("id");
String id = (String) idMethod.invoke(descriptor);
Method displayMethod = descriptor.getClass().getMethod("displayName");
String display = (String) displayMethod.invoke(descriptor);
ret.add(new ProcessDescription(id, display));
}
return ret;
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
throw new ProcessingException("Error while listing JVM processes", e, options);
}
}
/**
* Filter the process list for a regular expression and returns the description. The process this
* JVM is running in is ignored. If more than one process or no process is found, an exception
* is raised.
*
* @param pPattern regular expression to match
* @return a process description of the one process found but never null
* @throws IllegalArgumentException if more than one or no process has been found.
*/
@Override
public ProcessDescription findProcess(Pattern pPattern) {
return PlatformUtils.findProcess(pPattern, listProcesses());
}
@Override
public void loadAgent(Object pVm, String jarFilePath, String args) throws ProcessingException {
Class<?> clazz = pVm.getClass();
try {
Method method = clazz.getMethod("loadAgent",String.class, String.class);
method.invoke(pVm, jarFilePath, args);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
throw new ProcessingException("Error while loading Jolokia agent to a JVM process", e, options);
}
}
@Override
public Properties getSystemProperties(Object pVm) {
Class<?> clazz = pVm.getClass();
try {
Method method = clazz.getMethod("getSystemProperties");
return (Properties) method.invoke(pVm);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
throw new ProcessingException("Error while getting system properties from a JVM process", e, options);
}
}
// ========================================================================================================
// lookup virtual machine class
private Class<?> lookupVirtualMachineClass() {
try {
return ToolsClassFinder.lookupClass("com.sun.tools.attach.VirtualMachine");
} catch (ClassNotFoundException exp) {
throw new ProcessingException(
"Cannot find classes from tools.jar. The heuristics for loading tools.jar which contains\n" +
"essential classes (i.e. com.sun.tools.attach.VirtualMachine) for attaching to a running JVM\n" +
" ould not locate the necessary jar file.\n" +
"\n" +
"Please call this launcher with a qualified classpath on the command line like\n" +
"\n" +
" java -cp path/to/tools.jar:" + options.getJarFileName() + " org.jolokia.jvmagent.client.AgentLauncher [options] <command> <ppid>\n",
exp,
options);
}
}
}