@@ -1,12 +1,12 @@ | |||||
package com.jd.blockchain.contract.maven; | package com.jd.blockchain.contract.maven; | ||||
import com.jd.blockchain.contract.ContractJarUtils; | |||||
import com.jd.blockchain.contract.maven.rule.BlackList; | import com.jd.blockchain.contract.maven.rule.BlackList; | ||||
import com.jd.blockchain.contract.maven.rule.WhiteList; | import com.jd.blockchain.contract.maven.rule.WhiteList; | ||||
import com.jd.blockchain.contract.maven.rule.DependencyExclude; | import com.jd.blockchain.contract.maven.rule.DependencyExclude; | ||||
import com.jd.blockchain.contract.maven.verify.ResolveEngine; | import com.jd.blockchain.contract.maven.verify.ResolveEngine; | ||||
import com.jd.blockchain.contract.maven.verify.VerifyEngine; | import com.jd.blockchain.contract.maven.verify.VerifyEngine; | ||||
import org.apache.commons.io.FileUtils; | import org.apache.commons.io.FileUtils; | ||||
import org.apache.commons.io.IOUtils; | |||||
import org.apache.maven.artifact.Artifact; | import org.apache.maven.artifact.Artifact; | ||||
import org.apache.maven.plugin.MojoExecutionException; | import org.apache.maven.plugin.MojoExecutionException; | ||||
import org.apache.maven.plugin.MojoFailureException; | import org.apache.maven.plugin.MojoFailureException; | ||||
@@ -16,8 +16,14 @@ import org.apache.maven.project.MavenProject; | |||||
import java.io.File; | import java.io.File; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.util.ArrayList; | |||||
import java.util.Arrays; | |||||
import java.util.List; | |||||
import java.util.Set; | import java.util.Set; | ||||
import static com.jd.blockchain.contract.ContractJarUtils.BLACK_CONF; | |||||
import static com.jd.blockchain.contract.ContractJarUtils.WHITE_CONF; | |||||
@Mojo(name = "compile") | @Mojo(name = "compile") | ||||
public class ContractCompileMojo extends SingleAssemblyMojo { | public class ContractCompileMojo extends SingleAssemblyMojo { | ||||
@@ -42,46 +48,122 @@ public class ContractCompileMojo extends SingleAssemblyMojo { | |||||
// 首先对MainClass进行校验,要求必须有MainClass | // 首先对MainClass进行校验,要求必须有MainClass | ||||
String mainClass = mainClassVerify(); | String mainClass = mainClassVerify(); | ||||
// 将JDChain本身代码之外的代码移除(不打包进整个Jar) | |||||
// 排除所有依赖,只打包当前代码 | |||||
// excludeAllArtifactExclude(super.getProject().getDependencyArtifacts()); | |||||
// handleArtifactCompile(super.getProject().getDependencyArtifacts()); | |||||
handleArtifactExclude(super.getProject().getDependencyArtifacts()); | handleArtifactExclude(super.getProject().getDependencyArtifacts()); | ||||
// 此参数用于设置将所有第三方依赖的Jar包打散为.class,与主代码打包在一起,生成一个jar包 | // 此参数用于设置将所有第三方依赖的Jar包打散为.class,与主代码打包在一起,生成一个jar包 | ||||
super.setDescriptorRefs(new String[]{JAR_DEPENDENCE}); | super.setDescriptorRefs(new String[]{JAR_DEPENDENCE}); | ||||
// 执行打包命令 | // 执行打包命令 | ||||
// 该命令生成的是只含有当前项目的实际代码的Jar包,该Jar包仅用于校验MainClass | |||||
super.execute(); | super.execute(); | ||||
// 将本次打包好的文件重新命名,以便于后续重新打包需要 | |||||
// 把文件改名,然后重新再生成一个文件 | |||||
File dstFile; | |||||
// 生成解析引擎 | |||||
ResolveEngine resolveEngine = new ResolveEngine(getLog(), mainClass); | |||||
// 获取本次生成的Jar文件 | |||||
File defaultJarFile; | |||||
try { | try { | ||||
dstFile = rename(getProject(), getFinalName()); | |||||
} catch (IOException e) { | |||||
defaultJarFile = rename(getProject(), getFinalName()); | |||||
// 校验当前MainClass是否满足需求 | |||||
resolveEngine.verifyCurrentProjectMainClass(defaultJarFile); | |||||
// 校验完成后将该Jar删除 | |||||
// FileUtils.forceDelete(mainClassFile); | |||||
} catch (Exception e) { | |||||
getLog().error(e); | getLog().error(e); | ||||
throw new MojoFailureException(e.getMessage()); | throw new MojoFailureException(e.getMessage()); | ||||
} | } | ||||
// 首先校验该类的Jar包中是否包含不符合规范的命名,以及该类的代码中的部分解析 | |||||
File finalJarFile = verify(dstFile, mainClass); | |||||
// 将所有的依赖的jar包全部打包进一个包中,以便于进行ASM检查 | |||||
handleArtifactCompile(super.getProject().getDependencyArtifacts()); | |||||
// 然后再打包一次,本次打包完成后,其中的代码包含所有的class(JDK自身的除外) | |||||
super.execute(); | |||||
// 对代码中的一些规则进行校验,主要是校验其是否包含一些不允许使用的类、包、方法等 | |||||
verify(mainClass); | |||||
// 删除中间的一些文件 | |||||
// // 将JDChain本身之外的代码打包进Jar包,然后编译 | |||||
// handleArtifactExclude(super.getProject().getDependencyArtifacts()); | |||||
// | |||||
// // 此参数用于设置将所有第三方依赖的Jar包打散为.class,与主代码打包在一起,生成一个jar包 | |||||
// super.setDescriptorRefs(new String[]{JAR_DEPENDENCE}); | |||||
// | |||||
// // 生成Jar包(该Jar包中不包含JDChain内部的代码) | |||||
// super.execute(); | |||||
// | |||||
// File defaultJarFile; | |||||
// try { | |||||
// defaultJarFile = rename(getProject(), getFinalName()); | |||||
// } catch (Exception e) { | |||||
// getLog().error(e); | |||||
// throw new MojoFailureException(e.getMessage()); | |||||
// } | |||||
// 校验该Jar包 | |||||
verify(defaultJarFile, mainClass); | |||||
File deployJarFile = resolveEngine.verify(defaultJarFile); | |||||
// 删除中间产生的临时文件 | |||||
try { | try { | ||||
FileUtils.forceDelete(dstFile); | |||||
} catch (IOException e) { | |||||
throw new MojoFailureException(e.getMessage()); | |||||
FileUtils.forceDelete(defaultJarFile); | |||||
} catch (Exception e) { | |||||
getLog().error(e); | |||||
} | } | ||||
getLog().info(String.format("JDChain's Contract compile success, path = %s !", deployJarFile.getPath())); | |||||
// // 将JDChain本身代码之外的代码移除(不打包进整个Jar) | |||||
// handleArtifactExclude(super.getProject().getDependencyArtifacts()); | |||||
// | |||||
// // 此参数用于设置将所有第三方依赖的Jar包打散为.class,与主代码打包在一起,生成一个jar包 | |||||
// super.setDescriptorRefs(new String[]{JAR_DEPENDENCE}); | |||||
// | |||||
// // 执行打包命令 | |||||
// super.execute(); | |||||
// // 将本次打包好的文件重新命名,以便于后续重新打包需要 | |||||
// // 把文件改名,然后重新再生成一个文件 | |||||
// File dstFile; | |||||
// try { | |||||
// dstFile = rename(getProject(), getFinalName()); | |||||
// } catch (IOException e) { | |||||
// getLog().error(e); | |||||
// throw new MojoFailureException(e.getMessage()); | |||||
// } | |||||
// | |||||
// // dstFile理论上应该含有 | |||||
// | |||||
// // 首先校验该类的Jar包中是否包含不符合规范的命名,以及该类的代码中的部分解析 | |||||
// | |||||
// ResolveEngine resolveEngine = new ResolveEngine(getLog(), mainClass); | |||||
// | |||||
// // 校验mainClass | |||||
// resolveEngine.verifyCurrentProjectMainClass(dstFile); | |||||
// | |||||
// | |||||
// | |||||
// File finalJarFile = resolveEngine.verify(); | |||||
// | |||||
// // 将所有的依赖的jar包全部打包进一个包中,以便于进行ASM检查 | |||||
// handleArtifactCompile(super.getProject().getDependencyArtifacts()); | |||||
// | |||||
// // 然后再打包一次,本次打包完成后,其中的代码包含所有的class(JDK自身的除外) | |||||
// super.execute(); | |||||
// | |||||
// File jarFile = new File(jarPath(getProject(), getFinalName())); | |||||
// | |||||
// // 校验mainClass | |||||
// resolveEngine.verifyCurrentProjectMainClass(jarFile); | |||||
// | |||||
// // 对代码中的一些规则进行校验,主要是校验其是否包含一些不允许使用的类、包、方法等 | |||||
// verify(jarFile, mainClass); | |||||
// | |||||
// // 删除中间的一些文件 | |||||
//// try { | |||||
//// FileUtils.forceDelete(dstFile); | |||||
//// } catch (IOException e) { | |||||
//// throw new MojoFailureException(e.getMessage()); | |||||
//// } | |||||
// | |||||
// 若执行到此处没有异常则表明打包成功,打印打包成功消息 | // 若执行到此处没有异常则表明打包成功,打印打包成功消息 | ||||
getLog().info(String.format("JDChain's Contract compile success, path = %s !", finalJarFile.getPath())); | |||||
// getLog().info(String.format("JDChain's Contract compile success, path = %s !", finalJarFile.getPath())); | |||||
} | } | ||||
private String mainClassVerify() throws MojoFailureException { | private String mainClassVerify() throws MojoFailureException { | ||||
@@ -107,10 +189,20 @@ public class ContractCompileMojo extends SingleAssemblyMojo { | |||||
getLog().info(String.format("GroupId[%s] ArtifactId[%s] belongs to DependencyExclude !!!", groupId, artifactId)); | getLog().info(String.format("GroupId[%s] ArtifactId[%s] belongs to DependencyExclude !!!", groupId, artifactId)); | ||||
// 属于排除的名单之中 | // 属于排除的名单之中 | ||||
artifact.setScope(SCOPE_PROVIDED); | artifact.setScope(SCOPE_PROVIDED); | ||||
} else { | |||||
getLog().info(String.format("GroupId[%s] ArtifactId[%s] not belongs to DependencyExclude !!!", groupId, artifactId)); | |||||
// 属于排除的名单之中 | |||||
artifact.setScope(SCOPE_COMPILE); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
private void excludeAllArtifactExclude(Set<Artifact> artifacts) { | |||||
for (Artifact artifact : artifacts) { | |||||
artifact.setScope(SCOPE_PROVIDED); | |||||
} | |||||
} | |||||
private void handleArtifactCompile(Set<Artifact> artifacts) { | private void handleArtifactCompile(Set<Artifact> artifacts) { | ||||
for (Artifact artifact : artifacts) { | for (Artifact artifact : artifacts) { | ||||
if (artifact.getScope().equals(SCOPE_PROVIDED)) { | if (artifact.getScope().equals(SCOPE_PROVIDED)) { | ||||
@@ -135,17 +227,14 @@ public class ContractCompileMojo extends SingleAssemblyMojo { | |||||
File.separator + finalName + "-" + JAR_DEPENDENCE + ".jar"; | File.separator + finalName + "-" + JAR_DEPENDENCE + ".jar"; | ||||
} | } | ||||
private void verify(String mainClass) throws MojoFailureException { | |||||
private void verify(File jarFile, String mainClass) throws MojoFailureException { | |||||
try { | try { | ||||
File jarFile = new File(jarPath(getProject(), getFinalName())); | |||||
VerifyEngine verifyEngine = new VerifyEngine(getLog(), jarFile, mainClass, black, white); | VerifyEngine verifyEngine = new VerifyEngine(getLog(), jarFile, mainClass, black, white); | ||||
verifyEngine.verify(); | verifyEngine.verify(); | ||||
// 校验完成后将该jar包删除 | // 校验完成后将该jar包删除 | ||||
FileUtils.forceDelete(jarFile); | |||||
// FileUtils.forceDelete(jarFile); | |||||
} catch (Exception e) { | } catch (Exception e) { | ||||
getLog().error(e); | getLog().error(e); | ||||
@@ -153,20 +242,47 @@ public class ContractCompileMojo extends SingleAssemblyMojo { | |||||
} | } | ||||
} | } | ||||
private File verify(File jarFile, String mainClass) throws MojoFailureException { | |||||
ResolveEngine resolveEngine = new ResolveEngine(getLog(), jarFile, mainClass); | |||||
return resolveEngine.verify(); | |||||
private static void init() { | |||||
try { | |||||
black = AbstractContract.initBlack(loadBlackConf()); | |||||
white = AbstractContract.initWhite(loadWhiteConf()); | |||||
} catch (Exception e) { | |||||
throw new IllegalStateException(e); | |||||
} | |||||
} | |||||
private static List<String> loadWhiteConf() { | |||||
return resolveConfig(WHITE_CONF); | |||||
} | } | ||||
private static void init() { | |||||
private static List<String> loadBlackConf() { | |||||
return resolveConfig(BLACK_CONF); | |||||
} | |||||
private static List<String> resolveConfig(String fileName) { | |||||
List<String> configs = new ArrayList<>(); | |||||
try { | try { | ||||
black = AbstractContract.initBlack(ContractJarUtils.loadBlackConf()); | |||||
white = AbstractContract.initWhite(ContractJarUtils.loadWhiteConf()); | |||||
List<String> readLines = loadConfig(fileName); | |||||
if (!readLines.isEmpty()) { | |||||
for (String readLine : readLines) { | |||||
String[] lines = readLine.split(","); | |||||
configs.addAll(Arrays.asList(lines)); | |||||
} | |||||
} | |||||
} catch (Exception e) { | } catch (Exception e) { | ||||
throw new IllegalStateException(e); | throw new IllegalStateException(e); | ||||
} | } | ||||
return configs; | |||||
} | |||||
public static List<String> loadConfig(String fileName) throws Exception { | |||||
return IOUtils.readLines( | |||||
ContractCompileMojo.class.getResourceAsStream("/" + fileName)); | |||||
} | } | ||||
} | } |
@@ -52,6 +52,7 @@ public class BlackList { | |||||
} | } | ||||
public boolean isBlack(Class<?> clazz, String methodName) { | public boolean isBlack(Class<?> clazz, String methodName) { | ||||
// 判断该Class是否属于黑名单 | // 判断该Class是否属于黑名单 | ||||
if (isCurrentClassBlack(clazz, methodName)) { | if (isCurrentClassBlack(clazz, methodName)) { | ||||
return true; | return true; | ||||
@@ -75,7 +76,7 @@ public class BlackList { | |||||
String packageName = clazz.getPackage().getName(); | String packageName = clazz.getPackage().getName(); | ||||
for (String bp : blackPackages) { | for (String bp : blackPackages) { | ||||
if (packageName.equals(bp) || packageName.startsWith(bp)) { | |||||
if ((packageName + ".").equals(bp) || packageName.startsWith(bp)) { | |||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
@@ -1,6 +1,6 @@ | |||||
package com.jd.blockchain.contract.maven.rule; | package com.jd.blockchain.contract.maven.rule; | ||||
import com.jd.blockchain.contract.ContractJarUtils; | |||||
import com.jd.blockchain.contract.maven.ContractCompileMojo; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.List; | import java.util.List; | ||||
@@ -11,7 +11,7 @@ public class DependencyExclude { | |||||
private static final String COMMON_ARTIFACTID = "*"; | private static final String COMMON_ARTIFACTID = "*"; | ||||
private static final String CONFIG = "provided.conf"; | |||||
private static final String CONFIG = "providers.conf"; | |||||
private static final Map<String, List<Dependency>> DEPENDENCYS = new ConcurrentHashMap<>(); | private static final Map<String, List<Dependency>> DEPENDENCYS = new ConcurrentHashMap<>(); | ||||
@@ -24,7 +24,7 @@ public class DependencyExclude { | |||||
} | } | ||||
private static void init() throws Exception { | private static void init() throws Exception { | ||||
List<String> readLines = ContractJarUtils.loadConfig(CONFIG); | |||||
List<String> readLines = ContractCompileMojo.loadConfig(CONFIG); | |||||
if (!readLines.isEmpty()) { | if (!readLines.isEmpty()) { | ||||
for (String line : readLines) { | for (String line : readLines) { | ||||
// groupId/artifactId | // groupId/artifactId | ||||
@@ -1,7 +1,10 @@ | |||||
package com.jd.blockchain.contract.maven.verify; | package com.jd.blockchain.contract.maven.verify; | ||||
import com.alibaba.fastjson.JSON; | |||||
import com.jd.blockchain.contract.Contract; | |||||
import com.jd.blockchain.contract.ContractJarUtils; | import com.jd.blockchain.contract.ContractJarUtils; | ||||
import com.jd.blockchain.contract.ContractType; | import com.jd.blockchain.contract.ContractType; | ||||
import com.jd.blockchain.contract.EventProcessingAware; | |||||
import org.apache.commons.io.FileUtils; | import org.apache.commons.io.FileUtils; | ||||
import org.apache.maven.plugin.MojoFailureException; | import org.apache.maven.plugin.MojoFailureException; | ||||
import org.apache.maven.plugin.logging.Log; | import org.apache.maven.plugin.logging.Log; | ||||
@@ -21,23 +24,49 @@ public class ResolveEngine { | |||||
private Log LOGGER; | private Log LOGGER; | ||||
private File jarFile; | |||||
// private File jarFile; | |||||
private String mainClass; | private String mainClass; | ||||
public ResolveEngine(Log LOGGER, File jarFile, String mainClass) { | |||||
// public ResolveEngine(Log LOGGER, File jarFile, String mainClass) { | |||||
public ResolveEngine(Log LOGGER, String mainClass) { | |||||
this.LOGGER = LOGGER; | this.LOGGER = LOGGER; | ||||
this.jarFile = jarFile; | |||||
// this.jarFile = jarFile; | |||||
this.mainClass = mainClass; | this.mainClass = mainClass; | ||||
} | } | ||||
public File verify() throws MojoFailureException { | |||||
/** | |||||
* 校验当前项目中MainClass其是否满足JDChain合约写法 | |||||
* | |||||
* @param mainClassJarFile | |||||
* @throws MojoFailureException | |||||
*/ | |||||
public void verifyCurrentProjectMainClass(File mainClassJarFile) throws MojoFailureException { | |||||
// 校验MainClass | |||||
try { | try { | ||||
// 首先校验MainClass | |||||
ClassLoader classLoader = verifyMainClass(jarFile); | |||||
LOGGER.debug(String.format("Verify Jar [%s] 's MainClass start...", mainClassJarFile.getName())); | |||||
// 自定义ClassLoader,必须使用Thread.currentThread().getContextClassLoader() | |||||
// 保证其项目内部加载的Jar包无须再加载一次 | |||||
URLClassLoader classLoader = new URLClassLoader(new URL[]{mainClassJarFile.toURI().toURL()}, | |||||
Thread.currentThread().getContextClassLoader()); | |||||
// 从MainClass作为入口进行MainClass代码校验 | |||||
Class<?> mClass = classLoader.loadClass(mainClass); | |||||
ContractType.resolve(mClass); | |||||
// 校验完成后需要释放,否则无法删除该Jar文件 | |||||
classLoader.close(); | |||||
LOGGER.debug(String.format("Verify Jar [%s] 's MainClass end...", mainClassJarFile.getName())); | |||||
} catch (Exception e) { | |||||
throw new MojoFailureException(e.getMessage()); | |||||
} | |||||
} | |||||
public File verify(File defaultJarFile) throws MojoFailureException { | |||||
try { | |||||
// 检查jar包中所有的class的命名,要求其包名不能为com.jd.blockchain.* | // 检查jar包中所有的class的命名,要求其包名不能为com.jd.blockchain.* | ||||
LinkedList<String> totalClasses = loadAllClass(jarFile); | |||||
LinkedList<String> totalClasses = loadAllClass(defaultJarFile); | |||||
if (!totalClasses.isEmpty()) { | if (!totalClasses.isEmpty()) { | ||||
@@ -47,9 +76,8 @@ public class ResolveEngine { | |||||
LOGGER.debug(String.format("Verify Dependency Class[%s] start......", dotClassName)); | LOGGER.debug(String.format("Verify Dependency Class[%s] start......", dotClassName)); | ||||
// 获取其包名 | // 获取其包名 | ||||
Class<?> currentClass = classLoader.loadClass(dotClassName); | |||||
String packageName = currentClass.getPackage().getName(); | |||||
// 将class转换为包名 | |||||
String packageName = class2Package(dotClassName); | |||||
if (ContractJarUtils.isJDChainPackage(packageName)) { | if (ContractJarUtils.isJDChainPackage(packageName)) { | ||||
throw new IllegalStateException(String.format("Class[%s]'s package[%s] cannot start with %s !", | throw new IllegalStateException(String.format("Class[%s]'s package[%s] cannot start with %s !", | ||||
@@ -61,18 +89,23 @@ public class ResolveEngine { | |||||
} | } | ||||
// 处理完成之后,生成finalName-JDChain-Contract.jar | // 处理完成之后,生成finalName-JDChain-Contract.jar | ||||
return compileCustomJar(); | |||||
return compileCustomJar(defaultJarFile); | |||||
} catch (Exception e) { | } catch (Exception e) { | ||||
LOGGER.error(e.getMessage()); | LOGGER.error(e.getMessage()); | ||||
throw new MojoFailureException(e.getMessage()); | throw new MojoFailureException(e.getMessage()); | ||||
} | } | ||||
} | } | ||||
private File compileCustomJar() throws IOException { | |||||
private String class2Package(String dotClassName) { | |||||
return dotClassName.substring(0, dotClassName.lastIndexOf(".")); | |||||
} | |||||
private File compileCustomJar(File defaultJarFile) throws IOException { | |||||
String fileParentPath = jarFile.getParentFile().getPath(); | |||||
String fileParentPath = defaultJarFile.getParentFile().getPath(); | |||||
String jarFileName = jarFile.getName(); | |||||
String jarFileName = defaultJarFile.getName(); | |||||
String fileName = jarFileName.substring(0, jarFileName.lastIndexOf(".")); | String fileName = jarFileName.substring(0, jarFileName.lastIndexOf(".")); | ||||
@@ -80,13 +113,13 @@ public class ResolveEngine { | |||||
String dstJarPath = fileParentPath + File.separator + | String dstJarPath = fileParentPath + File.separator + | ||||
fileName + "-temp-" + System.currentTimeMillis() + ".jar"; | fileName + "-temp-" + System.currentTimeMillis() + ".jar"; | ||||
File srcJar = jarFile, dstJar = new File(dstJarPath); | |||||
File srcJar = defaultJarFile, dstJar = new File(dstJarPath); | |||||
LOGGER.debug(String.format("Jar from [%s] to [%s] Copying", jarFile.getPath(), dstJarPath)); | |||||
LOGGER.debug(String.format("Jar from [%s] to [%s] Copying", defaultJarFile.getPath(), dstJarPath)); | |||||
// 首先进行Copy处理 | // 首先进行Copy处理 | ||||
copy(srcJar, dstJar); | copy(srcJar, dstJar); | ||||
LOGGER.debug(String.format("Jar from [%s] to [%s] Copied", jarFile.getPath(), dstJarPath)); | |||||
LOGGER.debug(String.format("Jar from [%s] to [%s] Copied", defaultJarFile.getPath(), dstJarPath)); | |||||
byte[] txtBytes = contractMF(FileUtils.readFileToByteArray(dstJar)).getBytes(StandardCharsets.UTF_8); | byte[] txtBytes = contractMF(FileUtils.readFileToByteArray(dstJar)).getBytes(StandardCharsets.UTF_8); | ||||
@@ -128,6 +161,9 @@ public class ResolveEngine { | |||||
} | } | ||||
} | } | ||||
} | } | ||||
// Jar文件使用完成后需要关闭,否则可能会产生无法删除的问题 | |||||
jarFile.close(); | |||||
return allClass; | return allClass; | ||||
} | } | ||||
} | } |
@@ -52,7 +52,8 @@ public class VerifyEngine { | |||||
// 加载所有的jar,然后ASM获取MAP | // 加载所有的jar,然后ASM获取MAP | ||||
URL jarURL = jarFile.toURI().toURL(); | URL jarURL = jarFile.toURI().toURL(); | ||||
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{jarURL}); | |||||
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{jarURL}, | |||||
Thread.currentThread().getContextClassLoader()); | |||||
// 解析Jar包中所有的Class | // 解析Jar包中所有的Class | ||||
Map<String, ContractClass> allContractClasses = resolveClasses(jarClasses()); | Map<String, ContractClass> allContractClasses = resolveClasses(jarClasses()); | ||||
@@ -60,6 +61,8 @@ public class VerifyEngine { | |||||
// 开始处理MainClass | // 开始处理MainClass | ||||
verify(urlClassLoader, allContractClasses); | verify(urlClassLoader, allContractClasses); | ||||
// 校验完成后需要释放ClassLoader,否则无法删除该Jar包 | |||||
urlClassLoader.close(); | |||||
} | } | ||||
public void verify(URLClassLoader urlClassLoader, Map<String, ContractClass> allContractClasses) throws Exception { | public void verify(URLClassLoader urlClassLoader, Map<String, ContractClass> allContractClasses) throws Exception { | ||||
@@ -91,6 +94,8 @@ public class VerifyEngine { | |||||
// 将该方法设置为已处理 | // 将该方法设置为已处理 | ||||
haveManagedMethods.add(managedKey); | haveManagedMethods.add(managedKey); | ||||
String dotClassName = method.getDotClassName(); | String dotClassName = method.getDotClassName(); | ||||
Class<?> dotClass = urlClassLoader.loadClass(dotClassName); | Class<?> dotClass = urlClassLoader.loadClass(dotClassName); | ||||
if (dotClass == null) { | if (dotClass == null) { | ||||
@@ -137,8 +142,13 @@ public class VerifyEngine { | |||||
} | } | ||||
} else { | } else { | ||||
// 非URLClassLoader加载的类,只需要做判断即可 | // 非URLClassLoader加载的类,只需要做判断即可 | ||||
// 对于系统加载的类,其白名单优先级高于黑名单 | |||||
// 1、不再需要获取其方法; | // 1、不再需要获取其方法; | ||||
// 2、只需要判断黑名单不需要判断白名单 | |||||
// 首先判断是否为白名单 | |||||
if (white.isWhite(dotClass)) { | |||||
return; | |||||
} | |||||
// 然后判断其是否为黑名单 | |||||
if (black.isBlack(dotClass, method.getMethodName())) { | if (black.isBlack(dotClass, method.getMethodName())) { | ||||
throw new IllegalStateException(String.format("Class [%s] Method [%s] is Black !!!", dotClassName, method.getMethodName())); | throw new IllegalStateException(String.format("Class [%s] Method [%s] is Black !!!", dotClassName, method.getMethodName())); | ||||
} | } | ||||
@@ -187,8 +197,6 @@ public class VerifyEngine { | |||||
continue; | continue; | ||||
} | } | ||||
LOGGER.info(String.format("Resolve Class [%s] ...", className)); | |||||
ContractClass contractClass = new ContractClass(className); | ContractClass contractClass = new ContractClass(className); | ||||
ClassReader cr = new ClassReader(classContent); | ClassReader cr = new ClassReader(classContent); | ||||
cr.accept(new ASMClassVisitor(contractClass), ClassReader.SKIP_DEBUG); | cr.accept(new ASMClassVisitor(contractClass), ClassReader.SKIP_DEBUG); | ||||
@@ -1,5 +1,13 @@ | |||||
java.io.* | |||||
java.nio.* | |||||
java.io.File | |||||
java.io.InputStream | |||||
java.io.OutputStream | |||||
java.io.DataInput | |||||
java.io.DataOutput | |||||
java.io.Reader | |||||
java.io.Writer | |||||
java.io.Flushable | |||||
java.nio.channels.* | |||||
java.nio.file.* | |||||
java.net.* | java.net.* | ||||
java.sql.* | java.sql.* | ||||
java.lang.reflect.* | java.lang.reflect.* | ||||
@@ -1 +1,3 @@ | |||||
com.jd.blockchain.* | |||||
com.jd.blockchain.* | |||||
java.nio.charset.Charset | |||||
com.alibaba.fastjson.* |
@@ -13,9 +13,13 @@ import com.jd.blockchain.service.TransactionBatchResultHandle; | |||||
import com.jd.blockchain.storage.service.utils.MemoryKVStorage; | import com.jd.blockchain.storage.service.utils.MemoryKVStorage; | ||||
import com.jd.blockchain.transaction.TxBuilder; | import com.jd.blockchain.transaction.TxBuilder; | ||||
import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
import com.jd.blockchain.utils.io.BytesUtils; | |||||
import org.apache.commons.io.IOUtils; | |||||
import org.junit.Test; | import org.junit.Test; | ||||
import org.mockito.Mockito; | import org.mockito.Mockito; | ||||
import java.io.ByteArrayOutputStream; | |||||
import java.io.InputStream; | |||||
import java.util.Random; | import java.util.Random; | ||||
import static org.junit.Assert.*; | import static org.junit.Assert.*; | ||||
@@ -44,6 +48,9 @@ public class ContractInvokingTest { | |||||
// 采用基于内存的 Storage; | // 采用基于内存的 Storage; | ||||
private MemoryKVStorage storage = new MemoryKVStorage(); | private MemoryKVStorage storage = new MemoryKVStorage(); | ||||
// 用于测试的发布合约文件 | |||||
private static final String CONTRACT_JAR = "contract-JDChain-Contract.jar"; | |||||
@Test | @Test | ||||
public void test() { | public void test() { | ||||
// 初始化账本到指定的存储库; | // 初始化账本到指定的存储库; | ||||
@@ -82,7 +89,6 @@ public class ContractInvokingTest { | |||||
// 构建基于接口调用合约的交易请求,用于测试合约调用; | // 构建基于接口调用合约的交易请求,用于测试合约调用; | ||||
TxBuilder txBuilder = new TxBuilder(ledgerHash); | TxBuilder txBuilder = new TxBuilder(ledgerHash); | ||||
TestContract contractProxy = txBuilder.contract(contractAddress, TestContract.class); | TestContract contractProxy = txBuilder.contract(contractAddress, TestContract.class); | ||||
TestContract contractProxy1 = txBuilder.contract(contractAddress, TestContract.class); | |||||
String asset = "AK"; | String asset = "AK"; | ||||
long issueAmount = new Random().nextLong(); | long issueAmount = new Random().nextLong(); | ||||
@@ -190,8 +196,7 @@ public class ContractInvokingTest { | |||||
} | } | ||||
private byte[] chainCode() { | private byte[] chainCode() { | ||||
byte[] chainCode = new byte[1024]; | |||||
new Random().nextBytes(chainCode); | |||||
return chainCode; | |||||
return BytesUtils.copyToBytes(this.getClass().getResourceAsStream("/" + CONTRACT_JAR)); | |||||
} | } | ||||
} | } |
@@ -1,69 +0,0 @@ | |||||
package test.com.jd.blockchain.ledger.data; | |||||
import org.apache.commons.io.FileUtils; | |||||
import org.junit.Test; | |||||
import org.springframework.core.io.ClassPathResource; | |||||
import java.io.File; | |||||
import java.nio.charset.StandardCharsets; | |||||
import static com.jd.blockchain.contract.ContractJarUtils.*; | |||||
import static org.junit.Assert.fail; | |||||
public class ContractJarUtilsTest { | |||||
private String jarName = "complex"; | |||||
@Test | |||||
public void test() { | |||||
byte[] chainCode = null; | |||||
try { | |||||
ClassPathResource classPathResource = new ClassPathResource(jarName + ".jar"); | |||||
String classPath = classPathResource.getFile().getParentFile().getPath(); | |||||
// 首先将Jar包转换为指定的格式 | |||||
String srcJarPath = classPath + | |||||
File.separator + jarName + ".jar"; | |||||
String dstJarPath = classPath + | |||||
File.separator + jarName + "-temp-" + System.currentTimeMillis() + ".jar"; | |||||
File srcJar = new File(srcJarPath), dstJar = new File(dstJarPath); | |||||
// 首先进行Copy处理 | |||||
copy(srcJar, dstJar); | |||||
byte[] txtBytes = contractMF(FileUtils.readFileToByteArray(dstJar)).getBytes(StandardCharsets.UTF_8); | |||||
String finalJarPath = classPath + | |||||
File.separator + jarName + "-jdchain.jar"; | |||||
File finalJar = new File(finalJarPath); | |||||
copy(dstJar, finalJar, contractMFJarEntry(), txtBytes, null); | |||||
// 删除临时文件 | |||||
FileUtils.forceDelete(dstJar); | |||||
// 读取finalJar中的内容 | |||||
chainCode = FileUtils.readFileToByteArray(finalJar); | |||||
FileUtils.forceDelete(finalJar); | |||||
} catch (Exception e) { | |||||
e.printStackTrace(); | |||||
} | |||||
try { | |||||
verify(chainCode); | |||||
System.out.println("Verify Success !!!"); | |||||
} catch (Exception e) { | |||||
fail("Verify Fail !!"); | |||||
} | |||||
} | |||||
@Test | |||||
public void testSign() { | |||||
byte[] test = "zhangsan".getBytes(StandardCharsets.UTF_8); | |||||
System.out.println(contractMF(test)); | |||||
} | |||||
} |