Skip to content

Commit

Permalink
Merge pull request alibaba#92 from zhaoyb1990/feature/classloader_opt…
Browse files Browse the repository at this point in the history
…imize

开放模块指定类加载器路由能力
  • Loading branch information
oldmanpushcart authored Jun 21, 2018
2 parents da534bc + af0e018 commit 086b72d
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.alibaba.jvm.sandbox.api.routing;

/**
* <p>
* 提供给模块的扩展类路由白名单spi
*
* @author yuebing.zyb@alibaba-inc.com 2018/4/24 10:38.
*/
public interface RoutingExt {

/**
* 获取类的特殊路由方式
*
* @return 类路由方式
*/
RoutingInfo getSpecialRouting();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.alibaba.jvm.sandbox.api.routing;

/**
* <p>
* 提供给模块使用的目标类路由白名单,白名单的类不隔离
*
* 模块可以通过引入业务包进行编码
*
* 使用类的时候,调用业务的类加载器,保证模块可以正常使用业务系统中的类
*
* @author yuebing.zyb@alibaba-inc.com 2018/4/24 10:38.
*/
public class RoutingInfo {

/**
* 路由类型
*/
public enum Type {
/**
* 被pattern匹配的类,由目标的类的类加载器进行加载
*/
TARGET_CLASS,
/**
* 被pattern匹配的类,由目标类载器进行加载
*/
TARGET_CLASS_LOADER
}

/**
* 路由匹配正则表达式
*/
private String[] pattern;

/**
* 路由类型
*/
private RoutingInfo.Type type;

/**
* 目标类
*/
private String targetClass;

/**
* 目标类加载器
*/
private ClassLoader targetClassloader;

private RoutingInfo() {}

/**
* 使用目标类的加载器进行路由
*
* @param targetClass 目标类
* @param pattern 匹配类正则
*/
public RoutingInfo(String targetClass, String... pattern) {
this.type = Type.TARGET_CLASS;
this.pattern = pattern;
this.targetClass = targetClass;
}

/**
* 使用目标类的加载器进行路由
*
* @param classLoader
* @param pattern
*/
public RoutingInfo(ClassLoader classLoader, String... pattern) {
this.type = Type.TARGET_CLASS_LOADER;
this.pattern = pattern;
this.targetClassloader = classLoader;
}

public String[] getPattern() {
return pattern;
}

public Type getType() {
return type;
}

public String getTargetClass() {
return targetClass;
}

public ClassLoader getTargetClassloader() {
return targetClassloader;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@
import java.net.URLClassLoader;
import java.security.AccessControlContext;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarFile;

Expand All @@ -43,21 +46,22 @@ private static File copyToTempFile(final File moduleJarFile) throws IOException
}

public ModuleClassLoader(final File moduleJarFile,
final ClassLoader sandboxClassLoader) throws IOException {
this(moduleJarFile, copyToTempFile(moduleJarFile), sandboxClassLoader);
final ClassLoader sandboxClassLoader,
final Routing... routingArray) throws IOException {
this(moduleJarFile, copyToTempFile(moduleJarFile), sandboxClassLoader, routingArray);
}

private ModuleClassLoader(final File moduleJarFile,
final File tempModuleJarFile,
final ClassLoader sandboxClassLoader) throws IOException {
final ClassLoader sandboxClassLoader,
final Routing... routingArray) throws IOException {
super(
new URL[]{new URL("file:" + tempModuleJarFile.getPath())},
new Routing(
sandboxClassLoader,
"^com\\.alibaba\\.jvm\\.sandbox\\.api\\..*",
"^javax\\.servlet\\..*",
"^javax\\.annotation\\.Resource.*$"
)
new URL[] {new URL("file:" + tempModuleJarFile.getPath())},
assembleRouting(new Routing(sandboxClassLoader,
"^com\\.alibaba\\.jvm\\.sandbox\\.api\\..*",
"^javax\\.servlet\\..*",
"^javax\\.annotation\\.Resource.*$"
), routingArray)
);
this.checksumCRC32 = FileUtils.checksumCRC32(moduleJarFile);
this.moduleJarFile = moduleJarFile;
Expand All @@ -72,6 +76,22 @@ private ModuleClassLoader(final File moduleJarFile,

}

/**
* 组装路由表
*
* @param selfRouting 自身路由表
* @param routingArray 扩展路由表
* @return
*/
private static Routing[] assembleRouting(final Routing selfRouting, final Routing... routingArray) {
List<Routing> rs = new ArrayList<Routing>();
if (routingArray != null && routingArray.length > 0) {
rs.addAll(Arrays.asList(routingArray));
}
rs.add(selfRouting);
return rs.toArray(new Routing[rs.size()]);
}

/**
* 清理来自URLClassLoader.acc.ProtectionDomain[]中,来自上一个ModuleClassLoader的ProtectionDomain
* 这样写好蛋疼,而且还有不兼容的风险,从JDK6+都必须要这样清理,但我找不出更好的办法。
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import com.alibaba.jvm.sandbox.api.resource.ConfigInfo;
import com.alibaba.jvm.sandbox.core.CoreConfigure;
import com.alibaba.jvm.sandbox.core.server.ProxyCoreServer;
import com.alibaba.jvm.sandbox.core.server.jetty.JettyCoreServer;
import org.apache.commons.io.IOUtils;

import java.io.IOException;
Expand Down Expand Up @@ -116,7 +115,7 @@ public InetSocketAddress getServerAddress() {
try {
return ProxyCoreServer.getInstance().getLocal();
} catch (Throwable cause) {
return new InetSocketAddress("0.0.0.0", 0);
return new InetSocketAddress(cfg.getServerIp(), cfg.getServerPort());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ public synchronized void reset() throws ModuleException {
// 对模块访问权限进行校验
if (moduleLibDir.exists()
&& moduleLibDir.canRead()) {
new ModuleJarLoader(moduleLibDir, cfg.getLaunchMode(), sandboxClassLoader)
new ModuleJarLoader(moduleLibDir, cfg.getLaunchMode(), sandboxClassLoader, classDataSource)
.load(new InnerModuleJarLoadCallback(), new InnerModuleLoadCallback());
} else {
logger.warn("MODULE-LIB[{}] can not access, ignore flush load this lib.", moduleLibDir);
Expand Down Expand Up @@ -614,7 +614,7 @@ private void softFlush() throws ModuleException {

// 4. 加载add
for (final File jarFile : appendJarFiles) {
new ModuleJarLoader(jarFile, cfg.getLaunchMode(), sandboxClassLoader)
new ModuleJarLoader(jarFile, cfg.getLaunchMode(), sandboxClassLoader, classDataSource)
.load(new InnerModuleJarLoadCallback(), new InnerModuleLoadCallback());
}
} catch (Throwable cause) {
Expand Down Expand Up @@ -659,7 +659,7 @@ private void forceFlush() throws ModuleException {
for (final File userModuleLibDir : userModuleLibFileArray) {
if (userModuleLibDir.exists()
&& userModuleLibDir.canRead()) {
new ModuleJarLoader(userModuleLibDir, cfg.getLaunchMode(), sandboxClassLoader)
new ModuleJarLoader(userModuleLibDir, cfg.getLaunchMode(), sandboxClassLoader, classDataSource)
.load(new InnerModuleJarLoadCallback(), new InnerModuleLoadCallback());
} else {
logger.warn("MODULE-LIB[{}] can not access, ignore flush load this lib.", userModuleLibDir);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,23 @@

import com.alibaba.jvm.sandbox.api.Information;
import com.alibaba.jvm.sandbox.api.Module;
import com.alibaba.jvm.sandbox.api.resource.LoadedClassDataSource;
import com.alibaba.jvm.sandbox.api.routing.RoutingExt;
import com.alibaba.jvm.sandbox.api.routing.RoutingInfo;
import com.alibaba.jvm.sandbox.api.routing.RoutingInfo.Type;
import com.alibaba.jvm.sandbox.core.classloader.ModuleClassLoader;
import com.alibaba.jvm.sandbox.core.classloader.RoutingURLClassLoader.Routing;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceLoader;

import static org.apache.commons.io.FileUtils.convertFileCollectionToFileArray;
Expand All @@ -34,12 +42,16 @@ public class ModuleJarLoader {
// 沙箱加载ClassLoader
private final ClassLoader sandboxClassLoader;

private final LoadedClassDataSource loadedClassDataSource;

public ModuleJarLoader(final File moduleLibDir,
final Information.Mode mode,
final ClassLoader sandboxClassLoader) {
final ClassLoader sandboxClassLoader,
final LoadedClassDataSource loadedClassDataSource) {
this.moduleLibDir = moduleLibDir;
this.mode = mode;
this.sandboxClassLoader = sandboxClassLoader;
this.loadedClassDataSource = loadedClassDataSource;
}

private File[] toModuleJarFileArray() {
Expand Down Expand Up @@ -76,6 +88,32 @@ public void load(final ModuleJarLoadCallback mjCb,

ModuleClassLoader moduleClassLoader = null;

Routing[] routingArray = null;

/**
* 尝试获取路由列表
*/
try {
moduleClassLoader = new ModuleClassLoader(moduleJarFile, sandboxClassLoader);
ServiceLoader<RoutingExt> rExtServiceLoader = ServiceLoader.load(RoutingExt.class, moduleClassLoader);
Iterator<RoutingExt> iterator = rExtServiceLoader.iterator();
List<Routing> rs = new ArrayList<Routing>();
while (iterator.hasNext()) {
RoutingExt routingExt = iterator.next();
Routing routing = toRoutingRule(routingExt.getSpecialRouting());
if (routing != null) {
rs.add(routing);
}
}
routingArray = rs.toArray(new Routing[0]);
moduleClassLoader.closeIfPossible();
} catch (Throwable cause) {
logger.warn("load sandbox module JAR[file={}] failed.", moduleJarFile, cause);
if (null != moduleClassLoader) {
moduleClassLoader.closeIfPossible();
}
}

try {

// 是否有模块加载成功
Expand All @@ -86,7 +124,7 @@ public void load(final ModuleJarLoadCallback mjCb,
}

// 模块ClassLoader
moduleClassLoader = new ModuleClassLoader(moduleJarFile, sandboxClassLoader);
moduleClassLoader = new ModuleClassLoader(moduleJarFile, sandboxClassLoader, routingArray);

final ServiceLoader<Module> moduleServiceLoader = ServiceLoader.load(Module.class, moduleClassLoader);
final Iterator<Module> moduleIt = moduleServiceLoader.iterator();
Expand Down Expand Up @@ -151,6 +189,38 @@ public void load(final ModuleJarLoadCallback mjCb,

}

/**
* 转换成真正的路由表
*
* @param routingInfo 模块传递的路由
* @return
*/
private Routing toRoutingRule(final RoutingInfo routingInfo) {
if (routingInfo == null) {
return null;
}
// 自定义目标类加载器
if (routingInfo.getType() == Type.TARGET_CLASS_LOADER) {
ClassLoader classLoader = routingInfo.getTargetClassloader();
if (classLoader != null) {
logger.info("use target classloader routing rule,classloader={},pattern={}",classLoader,
routingInfo.getPattern());
return new Routing(classLoader, routingInfo.getPattern());
}
// 使用目标类的类加载器
} else if (routingInfo.getType() == Type.TARGET_CLASS) {
String className = routingInfo.getTargetClass();
for (Class<?> clazz : loadedClassDataSource.list()) {
if (StringUtils.equals(className, clazz.getName())) {
logger.info("find target routing rule,class={},classloader={},pattern={}",className,
clazz.getClassLoader(),routingInfo.getPattern());
return new Routing(clazz.getClassLoader(), routingInfo.getPattern());
}
}
}
return null;
}

/**
* 模块文件加载回调
*/
Expand Down

0 comments on commit 086b72d

Please sign in to comment.