-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
AgentLauncher.java
executable file
·417 lines (339 loc) · 14.8 KB
/
AgentLauncher.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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
package com.alibaba.jvm.sandbox.agent;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.net.InetSocketAddress;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static java.lang.String.format;
/**
* SandboxAgent启动器
* <ul>
* <li>这个类的所有静态属性都必须和版本、环境无关</li>
* <li>这个类删除、修改方法时必须考虑多版本情况下,兼容性问题!</li>
* </ul>
*
* @author luanjia@taobao.com
*/
public class AgentLauncher {
private static String getSandboxCfgPath(String sandboxHome) {
return sandboxHome + File.separatorChar + "cfg";
}
private static String getSandboxModulePath(String sandboxHome) {
return sandboxHome + File.separatorChar + "module";
}
private static String getSandboxCoreJarPath(String sandboxHome) {
return sandboxHome + File.separatorChar + "lib" + File.separator + "sandbox-core.jar";
}
private static String getSandboxSpyJarPath(String sandboxHome) {
return sandboxHome + File.separatorChar + "lib" + File.separator + "sandbox-spy.jar";
}
private static String getSandboxPropertiesPath(String sandboxHome) {
return getSandboxCfgPath(sandboxHome) + File.separator + "sandbox.properties";
}
private static String getSandboxProviderPath(String sandboxHome) {
return sandboxHome + File.separatorChar + "provider";
}
// sandbox默认主目录
private static final String DEFAULT_SANDBOX_HOME
= new File(AgentLauncher.class.getProtectionDomain().getCodeSource().getLocation().getFile())
.getParentFile()
.getParent();
private static final String SANDBOX_USER_MODULE_PATH
= DEFAULT_SANDBOX_HOME
+ File.separator + "sandbox-module";
// 启动模式: agent方式加载
private static final String LAUNCH_MODE_AGENT = "agent";
// 启动模式: attach方式加载
private static final String LAUNCH_MODE_ATTACH = "attach";
// 启动默认
private static String LAUNCH_MODE;
// agentmain上来的结果输出到文件${HOME}/.sandbox.token
private static final String RESULT_FILE_PATH = System.getProperties().getProperty("user.home")
+ File.separator + ".sandbox.token";
// 全局持有ClassLoader用于隔离sandbox实现
private static final Map<String/*NAMESPACE*/, SandboxClassLoader> sandboxClassLoaderMap
= new ConcurrentHashMap<>();
private static final String CLASS_OF_CORE_CONFIGURE = "com.alibaba.jvm.sandbox.core.CoreConfigure";
private static final String CLASS_OF_PROXY_CORE_SERVER = "com.alibaba.jvm.sandbox.core.server.ProxyCoreServer";
/**
* 启动加载
*
* @param featureString 启动参数
* [namespace,prop]
* @param inst inst
*/
public static void premain(String featureString, Instrumentation inst) {
LAUNCH_MODE = LAUNCH_MODE_AGENT;
install(toFeatureMap(featureString), inst);
}
/**
* 动态加载
*
* @param featureString 启动参数
* [namespace,token,ip,port,prop]
* @param inst inst
*/
public static void agentmain(String featureString, Instrumentation inst) {
LAUNCH_MODE = LAUNCH_MODE_ATTACH;
final Map<String, String> featureMap = toFeatureMap(featureString);
writeAttachResult(
getNamespace(featureMap),
getToken(featureMap),
install(featureMap, inst)
);
}
/**
* 写入本次attach的结果
* <p>
* NAMESPACE;TOKEN;IP;PORT
* </p>
*
* @param namespace 命名空间
* @param token 操作TOKEN
* @param local 服务器监听[IP:PORT]
*/
private static synchronized void writeAttachResult(final String namespace,
final String token,
final InetSocketAddress local) {
final File file = new File(RESULT_FILE_PATH);
if (file.exists()
&& (!file.isFile()
|| !file.canWrite())) {
throw new RuntimeException("write to result file : " + file + " failed.");
} else {
try (final FileWriter fw = new FileWriter(file, true)) {
fw.append(
format("%s;%s;%s;%s\n",
namespace,
token,
local.getHostName(),
local.getPort()
)
);
fw.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
// ignore
}
}
private static synchronized ClassLoader loadOrDefineClassLoader(final String namespace,
final String coreJar) throws Throwable {
final SandboxClassLoader classLoader;
// 如果已经被启动则返回之前启动的ClassLoader
if (sandboxClassLoaderMap.containsKey(namespace)
&& null != sandboxClassLoaderMap.get(namespace)) {
classLoader = sandboxClassLoaderMap.get(namespace);
}
// 如果未启动则重新加载
else {
classLoader = new SandboxClassLoader(namespace, coreJar);
sandboxClassLoaderMap.put(namespace, classLoader);
}
return classLoader;
}
/**
* 删除指定命名空间下的jvm-sandbox
*
* @param namespace 指定命名空间
* @throws Throwable 删除失败
*/
@SuppressWarnings("unused")
public static synchronized void uninstall(final String namespace) throws Throwable {
final SandboxClassLoader sandboxClassLoader = sandboxClassLoaderMap.get(namespace);
if (null == sandboxClassLoader) {
return;
}
// 关闭服务器
final Class<?> classOfProxyServer = sandboxClassLoader.loadClass(CLASS_OF_PROXY_CORE_SERVER);
classOfProxyServer.getMethod("destroy")
.invoke(classOfProxyServer.getMethod("getInstance").invoke(null));
// 关闭SandboxClassLoader
sandboxClassLoader.closeIfPossible();
sandboxClassLoaderMap.remove(namespace);
}
/**
* 在当前JVM安装jvm-sandbox
*
* @param featureMap 启动参数配置
* @param inst inst
* @return 服务器IP:PORT
*/
private static synchronized InetSocketAddress install(final Map<String, String> featureMap,
final Instrumentation inst) {
final String namespace = getNamespace(featureMap);
final String propertiesFilePath = getPropertiesFilePath(featureMap);
final String coreFeatureString = toFeatureString(featureMap);
try {
final String home = getSandboxHome(featureMap);
// 将Spy注入到BootstrapClassLoader
inst.appendToBootstrapClassLoaderSearch(new JarFile(new File(
getSandboxSpyJarPath(home)
// SANDBOX_SPY_JAR_PATH
)));
// 构造自定义的类加载器,尽量减少Sandbox对现有工程的侵蚀
final ClassLoader sandboxClassLoader = loadOrDefineClassLoader(
namespace,
getSandboxCoreJarPath(home)
// SANDBOX_CORE_JAR_PATH
);
// CoreConfigure类定义
final Class<?> classOfConfigure = sandboxClassLoader.loadClass(CLASS_OF_CORE_CONFIGURE);
// 反序列化成CoreConfigure类实例
final Object objectOfCoreConfigure = classOfConfigure.getMethod("toConfigure", String.class, String.class)
.invoke(null, coreFeatureString, propertiesFilePath);
// CoreServer类定义
final Class<?> classOfProxyServer = sandboxClassLoader.loadClass(CLASS_OF_PROXY_CORE_SERVER);
// 获取CoreServer单例
final Object objectOfProxyServer = classOfProxyServer
.getMethod("getInstance")
.invoke(null);
// CoreServer.isBind()
final boolean isBind = (Boolean) classOfProxyServer.getMethod("isBind").invoke(objectOfProxyServer);
// 如果未绑定,则需要绑定一个地址
if (!isBind) {
try {
classOfProxyServer
.getMethod("bind", classOfConfigure, Instrumentation.class)
.invoke(objectOfProxyServer, objectOfCoreConfigure, inst);
} catch (Throwable t) {
classOfProxyServer.getMethod("destroy").invoke(objectOfProxyServer);
throw t;
}
}
// 返回服务器绑定的地址
return (InetSocketAddress) classOfProxyServer
.getMethod("getLocal")
.invoke(objectOfProxyServer);
} catch (Throwable cause) {
throw new RuntimeException("sandbox attach failed.", cause);
}
}
// ----------------------------------------------- 以下代码用于配置解析 -----------------------------------------------
private static final String EMPTY_STRING = "";
private static final String KEY_SANDBOX_HOME = "home";
private static final String KEY_NAMESPACE = "namespace";
private static final String DEFAULT_NAMESPACE = "default";
private static final String KEY_SERVER_IP = "server.ip";
private static final String DEFAULT_IP = "0.0.0.0";
private static final String KEY_SERVER_PORT = "server.port";
private static final String DEFAULT_PORT = "0";
private static final String KEY_TOKEN = "token";
private static final String DEFAULT_TOKEN = EMPTY_STRING;
private static final String KEY_PROPERTIES_FILE_PATH = "prop";
private static boolean isNotBlankString(final String string) {
return null != string
&& string.length() > 0
&& !string.matches("^\\s*$");
}
private static boolean isBlankString(final String string) {
return !isNotBlankString(string);
}
private static String getDefaultString(final String string, final String defaultString) {
return isNotBlankString(string)
? string
: defaultString;
}
private static Map<String, String> toFeatureMap(final String featureString) {
final Map<String, String> featureMap = new LinkedHashMap<>();
// 不对空字符串进行解析
if (isBlankString(featureString)) {
return featureMap;
}
// KV对片段数组
final String[] kvPairSegmentArray = featureString.split(";");
if (kvPairSegmentArray.length == 0) {
return featureMap;
}
for (String kvPairSegmentString : kvPairSegmentArray) {
if (isBlankString(kvPairSegmentString)) {
continue;
}
final String[] kvSegmentArray = kvPairSegmentString.split("=");
if (kvSegmentArray.length != 2
|| isBlankString(kvSegmentArray[0])
|| isBlankString(kvSegmentArray[1])) {
continue;
}
featureMap.put(kvSegmentArray[0], kvSegmentArray[1]);
}
return featureMap;
}
private static String getDefault(final Map<String, String> map, final String key, final String defaultValue) {
return null != map
&& !map.isEmpty()
? getDefaultString(map.get(key), defaultValue)
: defaultValue;
}
private static final String OS = System.getProperty("os.name").toLowerCase();
private static boolean isWindows() {
return OS.contains("win");
}
// 获取主目录
private static String getSandboxHome(final Map<String, String> featureMap) {
String home = getDefault(featureMap, KEY_SANDBOX_HOME, DEFAULT_SANDBOX_HOME);
if( isWindows() ){
Matcher m = Pattern.compile("(?i)^[/\\\\]([a-z])[/\\\\]").matcher(home);
if( m.find() ){
home = m.replaceFirst("$1:/");
}
}
return home;
}
// 获取命名空间
private static String getNamespace(final Map<String, String> featureMap) {
return getDefault(featureMap, KEY_NAMESPACE, DEFAULT_NAMESPACE);
}
// 获取TOKEN
private static String getToken(final Map<String, String> featureMap) {
return getDefault(featureMap, KEY_TOKEN, DEFAULT_TOKEN);
}
// 获取容器配置文件路径
private static String getPropertiesFilePath(final Map<String, String> featureMap) {
return getDefault(
featureMap,
KEY_PROPERTIES_FILE_PATH,
getSandboxPropertiesPath(getSandboxHome(featureMap))
);
}
// 如果featureMap中有对应的key值,则将featureMap中的[K,V]对合并到featureSB中
private static void appendFromFeatureMap(final StringBuilder featureSB,
final Map<String, String> featureMap,
final String key,
final String defaultValue) {
if (featureMap.containsKey(key)) {
featureSB.append(format("%s=%s;", key, getDefault(featureMap, key, defaultValue)));
}
}
// 将featureMap中的[K,V]对转换为featureString
private static String toFeatureString(final Map<String, String> featureMap) {
final String sandboxHome = getSandboxHome(featureMap);
final StringBuilder featureSB = new StringBuilder(
format(
";cfg=%s;system_module=%s;mode=%s;sandbox_home=%s;user_module=%s;provider=%s;namespace=%s;",
getSandboxCfgPath(sandboxHome),
// SANDBOX_CFG_PATH,
getSandboxModulePath(sandboxHome),
// SANDBOX_MODULE_PATH,
LAUNCH_MODE,
sandboxHome,
// SANDBOX_HOME,
SANDBOX_USER_MODULE_PATH,
getSandboxProviderPath(sandboxHome),
// SANDBOX_PROVIDER_LIB_PATH,
getNamespace(featureMap)
)
);
// 合并IP(如有)
appendFromFeatureMap(featureSB, featureMap, KEY_SERVER_IP, DEFAULT_IP);
// 合并PORT(如有)
appendFromFeatureMap(featureSB, featureMap, KEY_SERVER_PORT, DEFAULT_PORT);
return featureSB.toString();
}
}