Skip to content

Commit

Permalink
修复SANDBOX从另外一个位置重新加载时,会因为AgentLauncher缓存了原有路径,导致无法从正确路径加载的BUG
Browse files Browse the repository at this point in the history
  • Loading branch information
杜琨 committed Mar 12, 2018
1 parent 7ea27c5 commit 07f2992
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 41 deletions.
6 changes: 3 additions & 3 deletions bin/sandbox.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# program : sandbox
# author : luanjia@taobao.com
# date : 2017-01-01
# version : 0.0.0.1
# date : 2018-03-12
# version : 0.0.0.2

#!/usr/bin/env bash

Expand Down Expand Up @@ -218,7 +218,7 @@ function attach_jvm() {
-jar ${SANDBOX_LIB_DIR}/sandbox-core.jar \
${TARGET_JVM_PID} \
"${SANDBOX_LIB_DIR}/sandbox-agent.jar" \
"token=${token};ip=${TARGET_SERVER_IP};port=${TARGET_SERVER_PORT};namespace=${TARGET_NAMESPACE}" \
"home=${SANDBOX_HOME_DIR};token=${token};ip=${TARGET_SERVER_IP};port=${TARGET_SERVER_PORT};namespace=${TARGET_NAMESPACE}" \
|| exit_on_err 1 "attach JVM ${TARGET_JVM_PID} fail."

# get network from attach result
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,43 +10,74 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarFile;

import static java.lang.String.format;

/**
* SandboxAgent启动器
* Created by luanjia@taobao.com on 16/7/30.
*/
public class AgentLauncher {

// sandbox主目录
private static final String SANDBOX_HOME
= new File(AgentLauncher.class.getProtectionDomain().getCodeSource().getLocation().getFile())
.getParentFile().getParent();
// // sandbox配置文件目录
// private static final String SANDBOX_CFG_PATH
// = SANDBOX_HOME + File.separatorChar + "cfg";
//
// // 模块目录
// private static final String SANDBOX_MODULE_PATH
// = SANDBOX_HOME + File.separatorChar + "module";
//
//
// // sandbox核心工程文件
// private static final String SANDBOX_CORE_JAR_PATH
// = SANDBOX_HOME + File.separatorChar + "lib" + File.separator + "sandbox-core.jar";
//
// // sandbox-spy工程文件
// private static final String SANDBOX_SPY_JAR_PATH
// = SANDBOX_HOME + File.separatorChar + "lib" + File.separator + "sandbox-spy.jar";
//
// private static final String SANDBOX_PROPERTIES_PATH
// = SANDBOX_CFG_PATH + File.separator + "sandbox.properties";
//
// // sandbox-provider库目录
// private static final String SANDBOX_PROVIDER_LIB_PATH
// = SANDBOX_HOME + File.separatorChar + "provider";


private static String getSandboxCfgPath(String sandboxHome) {
return sandboxHome + File.separatorChar + "cfg";
}

private static String getSandboxModulePath(String sandboxHome) {
return sandboxHome + File.separatorChar + "module";
}

// sandbox配置文件目录
private static final String SANDBOX_CFG_PATH
= SANDBOX_HOME + File.separatorChar + "cfg";
private static String getSandboxCoreJarPath(String sandboxHome) {
return sandboxHome + File.separatorChar + "lib" + File.separator + "sandbox-core.jar";
}

// 模块目录
private static final String SANDBOX_MODULE_PATH
= SANDBOX_HOME + File.separatorChar + "module";
private static String getSandboxSpyJarPath(String sandboxHome) {
return getSandboxCfgPath(sandboxHome) + File.separatorChar + "lib" + File.separator + "sandbox-spy.jar";
}

private static final String SANDBOX_USER_MODULE_PATH
= System.getProperties().getProperty("user.home")
+ File.separator + ".sandbox-module";
private static String getSandboxPropertiesPath(String sandboxHome) {
return sandboxHome + File.separator + "sandbox.properties";
}

// sandbox核心工程文件
private static final String SANDBOX_CORE_JAR_PATH
= SANDBOX_HOME + File.separatorChar + "lib" + File.separator + "sandbox-core.jar";
private static String getSandboxProviderPath(String sandboxHome) {
return sandboxHome + File.separatorChar + "provider";
}

// sandbox-spy工程文件
private static final String SANDBOX_SPY_JAR_PATH
= SANDBOX_HOME + File.separatorChar + "lib" + File.separator + "sandbox-spy.jar";

private static final String SANDBOX_PROPERTIES_PATH
= SANDBOX_CFG_PATH + File.separator + "sandbox.properties";
// sandbox默认主目录
private static final String SANDBOX_HOME
= new File(AgentLauncher.class.getProtectionDomain().getCodeSource().getLocation().getFile())
.getParentFile()
.getParent();
private static final String DEFAULT_SANDBOX_HOME = SANDBOX_HOME;

// sandbox-provider库目录
private static final String SANDBOX_PROVIDER_LIB_PATH
= SANDBOX_HOME + File.separatorChar + "provider";
private static final String SANDBOX_USER_MODULE_PATH
= System.getProperties().getProperty("user.home")
+ File.separator + ".sandbox-module";

// 启动模式: agent方式加载
private static final String LAUNCH_MODE_AGENT = "agent";
Expand Down Expand Up @@ -122,7 +153,7 @@ private static synchronized void writeAttachResult(final String namespace,
try {
fw = new FileWriter(file, true);
fw.append(
String.format("%s;%s;%s;%s\n",
format("%s;%s;%s;%s\n",
namespace,
token,
local.getHostName(),
Expand Down Expand Up @@ -167,21 +198,29 @@ private static synchronized ClassLoader loadOrDefineClassLoader(final String nam

/**
* 获取当前命名空间下的ClassLoader
* <p>
* 该方法将会被{@code ControlModule#shutdown}通过反射调用,
* 请保持方法声明一致
*
* @param namespace 命名空间
* @return 当前的ClassLoader
* @since {@code sandbox-api:1.0.15}
*/
@SuppressWarnings("unused")
public static ClassLoader getClassLoader(final String namespace) {
return sandboxClassLoaderMap.get(namespace);
}

/**
* 清理namespace所指定的ClassLoader
* <p>
* 该方法将会被{@code ControlModule#shutdown}通过反射调用,
* 请保持方法声明一致
*
* @param namespace 命名空间
* @return 被清理的ClassLoader
*/
@SuppressWarnings("unused")
public static synchronized ClassLoader cleanClassLoader(final String namespace) {
final SandboxClassLoader sandboxClassLoader = sandboxClassLoaderMap.remove(namespace);
if (null != sandboxClassLoader) {
Expand All @@ -200,10 +239,17 @@ private static synchronized InetSocketAddress main(final Map<String, String> fea
try {

// 将Spy注入到BootstrapClassLoader
inst.appendToBootstrapClassLoaderSearch(new JarFile(new File(SANDBOX_SPY_JAR_PATH)));
inst.appendToBootstrapClassLoaderSearch(new JarFile(new File(
getSandboxSpyJarPath(getSandboxHome(featureMap))
// SANDBOX_SPY_JAR_PATH
)));

// 构造自定义的类加载器,尽量减少Sandbox对现有工程的侵蚀
final ClassLoader agentLoader = loadOrDefineClassLoader(namespace, SANDBOX_CORE_JAR_PATH);
final ClassLoader agentLoader = loadOrDefineClassLoader(
namespace,
getSandboxCoreJarPath(getSandboxHome(featureMap))
// SANDBOX_CORE_JAR_PATH
);

// CoreConfigure类定义
final Class<?> classOfConfigure = agentLoader.loadClass(CLASS_OF_CORE_CONFIGURE);
Expand Down Expand Up @@ -254,6 +300,8 @@ private static synchronized InetSocketAddress main(final Map<String, String> fea

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";

Expand Down Expand Up @@ -294,8 +342,7 @@ private static Map<String, String> toFeatureMap(final String featureString) {

// KV对片段数组
final String[] kvPairSegmentArray = featureString.split(";");
if (null == kvPairSegmentArray
|| kvPairSegmentArray.length <= 0) {
if (kvPairSegmentArray.length <= 0) {
return featureMap;
}

Expand All @@ -304,8 +351,7 @@ private static Map<String, String> toFeatureMap(final String featureString) {
continue;
}
final String[] kvSegmentArray = kvPairSegmentString.split("=");
if (null == kvSegmentArray
|| kvSegmentArray.length != 2
if (kvSegmentArray.length != 2
|| isBlankString(kvSegmentArray[0])
|| isBlankString(kvSegmentArray[1])) {
continue;
Expand All @@ -323,6 +369,11 @@ private static String getDefault(final Map<String, String> map, final String key
: defaultValue;
}

// 获取主目录
private static String getSandboxHome(final Map<String, String> featureMap) {
return getDefault(featureMap, KEY_SANDBOX_HOME, DEFAULT_SANDBOX_HOME);
}

// 获取命名空间
private static String getNamespace(final Map<String, String> featureMap) {
return getDefault(featureMap, KEY_NAMESPACE, DEFAULT_NAMESPACE);
Expand All @@ -335,7 +386,12 @@ private static String getToken(final Map<String, String> featureMap) {

// 获取容器配置文件路径
private static String getPropertiesFilePath(final Map<String, String> featureMap) {
return getDefault(featureMap, KEY_PROPERTIES_FILE_PATH, SANDBOX_PROPERTIES_PATH);
return getDefault(
featureMap,
KEY_PROPERTIES_FILE_PATH,
getSandboxPropertiesPath(getSandboxHome(featureMap))
// SANDBOX_PROPERTIES_PATH
);
}

// 如果featureMap中有对应的key值,则将featureMap中的[K,V]对合并到featureSB中
Expand All @@ -344,21 +400,26 @@ private static void appendFromFeatureMap(final StringBuilder featureSB,
final String key,
final String defaultValue) {
if (featureMap.containsKey(key)) {
featureSB.append(String.format("%s=%s;", key, getDefault(featureMap, key, defaultValue)));
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(
String.format(
format(
";cfg=%s;system_module=%s;mode=%s;sandbox_home=%s;user_module=%s;provider=%s;namespace=%s;",
SANDBOX_CFG_PATH,
SANDBOX_MODULE_PATH,
getSandboxCfgPath(sandboxHome),
// SANDBOX_CFG_PATH,
getSandboxModulePath(sandboxHome),
// SANDBOX_MODULE_PATH,
LAUNCH_MODE,
SANDBOX_HOME,
sandboxHome,
// SANDBOX_HOME,
SANDBOX_USER_MODULE_PATH,
SANDBOX_PROVIDER_LIB_PATH,
getSandboxProviderPath(sandboxHome),
// SANDBOX_PROVIDER_LIB_PATH,
getNamespace(featureMap)
)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@ public String toString() {
}


/**
* 尽可能关闭ClassLoader
* <p>
* URLClassLoader会打开指定的URL资源,在SANDBOX中则是对应的Jar文件,如果不在shutdown的时候关闭ClassLoader,会导致下次再次加载
* 的时候,依然会访问到上次所打开的文件(底层被缓存起来了)
* <p>
* 在JDK1.7版本中,URLClassLoader提供了{@code close()}方法来完成这件事;但在JDK1.6版本就要下点手段了;
* <p>
* 该方法将会被{@code ControlModule#shutdown}通过反射调用,
* 请保持方法声明一致
*/
@SuppressWarnings("unused")
public void closeIfPossible() {

// 如果是JDK7+的版本, URLClassLoader实现了Closeable接口,直接调用即可
Expand Down

0 comments on commit 07f2992

Please sign in to comment.