| @@ -90,6 +90,7 @@ public interface DataCodes { | |||
| public static final int CONTRACT_BIG_INT = 0xA07; | |||
| //...0xA19 | |||
| public static final int CONTRACT_BIZ_CONTENT = 0xA20; | |||
| public static final int CONTRACT_ARGS = 0xA21; | |||
| public static final int HASH = 0xB00; | |||
| @@ -10,6 +10,10 @@ | |||
| <artifactId>contract-maven-plugin</artifactId> | |||
| <packaging>maven-plugin</packaging> | |||
| <properties> | |||
| <maven.version>3.3.9</maven.version> | |||
| </properties> | |||
| <dependencies> | |||
| <dependency> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| @@ -35,32 +39,54 @@ | |||
| <version>${project.version}</version> | |||
| </dependency> | |||
| <!-- https://mvnrepository.com/artifact/org.apache.maven.plugin-tools/maven-plugin-annotations --> | |||
| <dependency> | |||
| <groupId>org.apache.maven.plugin-tools</groupId> | |||
| <artifactId>maven-plugin-annotations</artifactId> | |||
| <version>3.6.0</version> | |||
| <scope>provided</scope> | |||
| <groupId>com.github.javaparser</groupId> | |||
| <artifactId>javaparser-core</artifactId> | |||
| <version>${javaparser.version}</version> | |||
| </dependency> | |||
| <!-- new --> | |||
| <dependency> | |||
| <groupId>org.apache.maven</groupId> | |||
| <artifactId>maven-plugin-api</artifactId> | |||
| <version>2.0</version> | |||
| <version>3.3.9</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.apache.maven</groupId> | |||
| <artifactId>maven-project</artifactId> | |||
| <version>2.0.6</version> | |||
| <artifactId>maven-core</artifactId> | |||
| <version>3.3.9</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.github.javaparser</groupId> | |||
| <artifactId>javaparser-core</artifactId> | |||
| <version>${javaparser.version}</version> | |||
| <groupId>org.apache.maven</groupId> | |||
| <artifactId>maven-artifact</artifactId> | |||
| <version>3.3.9</version> | |||
| <scope>provided</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.apache.maven</groupId> | |||
| <artifactId>maven-compat</artifactId> | |||
| <version>3.3.9</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>junit</groupId> | |||
| <artifactId>junit</artifactId> | |||
| <version>4.12</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.apache.maven.plugin-testing</groupId> | |||
| <artifactId>maven-plugin-testing-harness</artifactId> | |||
| <scope>test</scope> | |||
| <version>3.3.0</version> | |||
| </dependency> | |||
| <!-- https://mvnrepository.com/artifact/org.apache.maven.plugin-tools/maven-plugin-annotations --> | |||
| <dependency> | |||
| <groupId>org.apache.maven.plugin-tools</groupId> | |||
| <artifactId>maven-plugin-annotations</artifactId> | |||
| <version>3.6.0</version> | |||
| <scope>provided</scope> | |||
| </dependency> | |||
| </dependencies> | |||
| <build> | |||
| @@ -4,6 +4,12 @@ import com.github.javaparser.JavaParser; | |||
| import com.github.javaparser.ast.CompilationUnit; | |||
| import com.github.javaparser.ast.ImportDeclaration; | |||
| import com.github.javaparser.ast.NodeList; | |||
| import com.github.javaparser.ast.PackageDeclaration; | |||
| import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; | |||
| import com.github.javaparser.ast.body.MethodDeclaration; | |||
| import com.github.javaparser.ast.visitor.VoidVisitorAdapter; | |||
| import com.jd.blockchain.transaction.ContractType; | |||
| import com.jd.blockchain.utils.IllegalDataException; | |||
| import org.apache.maven.plugin.AbstractMojo; | |||
| import org.apache.maven.plugin.MojoFailureException; | |||
| import org.apache.maven.plugins.annotations.Mojo; | |||
| @@ -12,15 +18,28 @@ import org.apache.maven.project.MavenProject; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import java.io.File; | |||
| import java.io.IOException; | |||
| import java.io.InputStream; | |||
| import java.net.URL; | |||
| import java.net.URLClassLoader; | |||
| import java.nio.file.Files; | |||
| import java.nio.file.Path; | |||
| import java.util.List; | |||
| import java.util.Properties; | |||
| import java.util.jar.Attributes; | |||
| import java.util.jar.JarFile; | |||
| import java.util.stream.Collectors; | |||
| /** | |||
| * first step, we want to parse the source code by javaParse. But it's repeated and difficult to parse the source. | |||
| * This is a try of "from Initail to Abandoned". | |||
| * Since we are good at the class, why not? | |||
| * Now we change a way of thinking, first we pre-compile the source code, then parse the *.jar. | |||
| * | |||
| * by zhaogw | |||
| * date 2019-06-05 16:17 | |||
| */ | |||
| @Mojo(name = "checkImports") | |||
| public class CheckImportsMojo extends AbstractMojo { | |||
| Logger logger = LoggerFactory.getLogger(CheckImportsMojo.class); | |||
| @@ -28,6 +47,12 @@ public class CheckImportsMojo extends AbstractMojo { | |||
| @Parameter(defaultValue = "${project}", required = true, readonly = true) | |||
| private MavenProject project; | |||
| /** | |||
| * jar's name; | |||
| */ | |||
| @Parameter | |||
| private String finalName; | |||
| @Override | |||
| public void execute() throws MojoFailureException { | |||
| List<Path> sources; | |||
| @@ -40,6 +65,9 @@ public class CheckImportsMojo extends AbstractMojo { | |||
| sources = Files.find(baseDirPath, Integer.MAX_VALUE, (file, attrs) -> (file.toString().endsWith(".java"))).collect(Collectors.toList()); | |||
| for (Path path : sources) { | |||
| CompilationUnit compilationUnit = JavaParser.parse(path); | |||
| compilationUnit.accept(new MethodVisitor(), null); | |||
| NodeList<ImportDeclaration> imports = compilationUnit.getImports(); | |||
| for (ImportDeclaration imp : imports) { | |||
| String importName = imp.getName().asString(); | |||
| @@ -49,6 +77,20 @@ public class CheckImportsMojo extends AbstractMojo { | |||
| } | |||
| } | |||
| } | |||
| //now we parse the jar; | |||
| String jarPath = project.getBuild().getDirectory()+ File.separator+finalName+".jar"; | |||
| File jarFile = new File(jarPath); | |||
| URL jarURL = jarFile.toURI().toURL(); | |||
| ClassLoader classLoader = new URLClassLoader(new URL[]{jarURL},this.getClass().getClassLoader()); | |||
| Attributes m = new JarFile(jarFile).getManifest().getMainAttributes(); | |||
| String contractMainClass = m.getValue(Attributes.Name.MAIN_CLASS); | |||
| try { | |||
| Class mainClass = classLoader.loadClass(contractMainClass); | |||
| ContractType.resolve(mainClass); | |||
| } catch (ClassNotFoundException e) { | |||
| throw new IllegalDataException(e.getMessage()); | |||
| } | |||
| } | |||
| } catch (IOException exception) { | |||
| logger.error(exception.getMessage()); | |||
| @@ -57,4 +99,29 @@ public class CheckImportsMojo extends AbstractMojo { | |||
| logger.error(e.getMessage()); | |||
| } | |||
| } | |||
| private class MethodVisitor extends VoidVisitorAdapter<Void> { | |||
| @Override | |||
| public void visit(MethodDeclaration n, Void arg) { | |||
| /* here you can access the attributes of the method. | |||
| this method will be called for all methods in this | |||
| CompilationUnit, including inner class methods */ | |||
| logger.info("method:"+n.getName()); | |||
| super.visit(n, arg); | |||
| } | |||
| @Override | |||
| public void visit(ClassOrInterfaceDeclaration n, Void arg) { | |||
| logger.info("class:"+n.getName()+" extends:"+n.getExtendedTypes()+" implements:"+n.getImplementedTypes()); | |||
| super.visit(n, arg); | |||
| } | |||
| @Override | |||
| public void visit(PackageDeclaration n, Void arg) { | |||
| logger.info("package:"+n.getName()); | |||
| super.visit(n, arg); | |||
| } | |||
| } | |||
| } | |||
| @@ -1,32 +1,28 @@ | |||
| package com.jd.blockchain.ledger; | |||
| import com.jd.blockchain.CheckImportsMojo; | |||
| import org.apache.maven.plugin.testing.AbstractMojoTestCase; | |||
| import org.junit.Test; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import java.io.IOException; | |||
| import java.io.InputStream; | |||
| import java.util.Arrays; | |||
| import java.util.Properties; | |||
| import java.io.File; | |||
| /** | |||
| * @Author zhaogw | |||
| * @Date 2019/3/1 21:27 | |||
| */ | |||
| public class CheckImportsMojoTest { | |||
| Logger logger = LoggerFactory.getLogger(CheckImportsMojo.class); | |||
| public class CheckImportsMojoTest extends AbstractMojoTestCase { | |||
| Logger logger = LoggerFactory.getLogger(CheckImportsMojoTest.class); | |||
| @Test | |||
| public void test1() { | |||
| try { | |||
| InputStream inputStream = CheckImportsMojo.class.getClassLoader().getResourceAsStream("config.properties"); | |||
| Properties properties = new Properties(); | |||
| properties.load(inputStream); | |||
| String result[] = properties.getProperty("blacklist").split(","); | |||
| logger.info(Arrays.toString(result).toString()); | |||
| } catch (IOException e) { | |||
| logger.error(e.getMessage()); | |||
| } | |||
| public void test1() throws Exception { | |||
| File pom = getTestFile( "src/test/resources/project-to-test/pom.xml" ); | |||
| assertNotNull( pom ); | |||
| assertTrue( pom.exists() ); | |||
| CheckImportsMojo myMojo = (CheckImportsMojo) lookupMojo( "checkImports", pom ); | |||
| assertNotNull( myMojo ); | |||
| myMojo.execute(); | |||
| } | |||
| } | |||
| @@ -1,10 +1,8 @@ | |||
| package com.jd.blockchain.ledger; | |||
| import com.jd.blockchain.ContractDeployMojo; | |||
| import org.junit.Test; | |||
| import java.lang.reflect.Field; | |||
| import java.lang.reflect.InvocationTargetException; | |||
| /** | |||
| * for contract deploy and exe; | |||
| @@ -14,8 +12,7 @@ import java.lang.reflect.InvocationTargetException; | |||
| public class ContractDeployMojoTest { | |||
| private ContractDeployMojo contractDeployMojo = new ContractDeployMojo(); | |||
| private void fieldHandle(String fieldName,Object objValue) throws NoSuchFieldException, | |||
| IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException { | |||
| private void fieldHandle(String fieldName,Object objValue) throws NoSuchFieldException, IllegalAccessException { | |||
| Field field = contractDeployMojo.getClass().getDeclaredField(fieldName);//name为类Instance中的private属性 | |||
| field.setAccessible(true);//=true,可访问私有变量。 | |||
| Class<?> typeClass = field.getType(); | |||
| @@ -0,0 +1,67 @@ | |||
| package com.jd.blockchain.ledger; | |||
| import org.apache.maven.model.Build; | |||
| import org.apache.maven.model.Model; | |||
| import org.apache.maven.model.io.xpp3.MavenXpp3Reader; | |||
| import org.apache.maven.plugin.testing.stubs.MavenProjectStub; | |||
| import org.codehaus.plexus.util.ReaderFactory; | |||
| import java.io.File; | |||
| import java.util.ArrayList; | |||
| import java.util.List; | |||
| /** | |||
| * @author zhaogw | |||
| * date 2019/6/4 18:33 | |||
| */ | |||
| public class MyProjectStub extends MavenProjectStub | |||
| { | |||
| /** | |||
| * Default constructor | |||
| */ | |||
| public MyProjectStub() | |||
| { | |||
| MavenXpp3Reader pomReader = new MavenXpp3Reader(); | |||
| Model model; | |||
| try | |||
| { | |||
| model = pomReader.read( ReaderFactory.newXmlReader( new File( getBasedir(), "pom.xml" ) ) ); | |||
| setModel( model ); | |||
| } | |||
| catch ( Exception e ) | |||
| { | |||
| throw new RuntimeException( e ); | |||
| } | |||
| setGroupId( model.getGroupId() ); | |||
| setArtifactId( model.getArtifactId() ); | |||
| setVersion( model.getVersion() ); | |||
| setName( model.getName() ); | |||
| setUrl( model.getUrl() ); | |||
| setPackaging( model.getPackaging() ); | |||
| Build build = new Build(); | |||
| build.setFinalName( model.getArtifactId() ); | |||
| build.setDirectory( getBasedir() + "/target" ); | |||
| build.setSourceDirectory( getBasedir() + "/src/main/java" ); | |||
| build.setOutputDirectory( getBasedir() + "/target/classes" ); | |||
| build.setTestSourceDirectory( getBasedir() + "/src/test/java" ); | |||
| build.setTestOutputDirectory( getBasedir() + "/target/test-classes" ); | |||
| setBuild( build ); | |||
| List compileSourceRoots = new ArrayList(); | |||
| compileSourceRoots.add( getBasedir() + "/src/main/java" ); | |||
| setCompileSourceRoots( compileSourceRoots ); | |||
| List testCompileSourceRoots = new ArrayList(); | |||
| testCompileSourceRoots.add( getBasedir() + "/src/test/java" ); | |||
| setTestCompileSourceRoots( testCompileSourceRoots ); | |||
| } | |||
| /** {@inheritDoc} */ | |||
| public File getBasedir() | |||
| { | |||
| return new File( super.getBasedir() + "/src/test/resources/project-to-test/" ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,40 @@ | |||
| package com.jd.blockchain.contract; | |||
| import com.jd.blockchain.ledger.ContractBizContent; | |||
| import com.jd.blockchain.utils.Bytes; | |||
| /** | |||
| * 示例:一个“资产管理”智能合约; | |||
| * | |||
| * @author zhaogw | |||
| */ | |||
| @Contract | |||
| public interface AssetContract2 { | |||
| /** | |||
| * 发行资产; | |||
| * 新发行的资产数量; | |||
| * @param assetHolderAddress | |||
| * 新发行的资产的持有账户; | |||
| */ | |||
| @ContractEvent(name = "issue-asset-0") | |||
| void issue(ContractBizContent contractBizContent, String assetHolderAddress); | |||
| /** | |||
| * 发行资产; | |||
| * 新发行的资产数量; | |||
| * @param assetHolderAddress | |||
| * 新发行的资产的持有账户; | |||
| */ | |||
| @ContractEvent(name = "issue-asset") | |||
| void issue(ContractBizContent contractBizContent, String assetHolderAddress, long cashNumber); | |||
| @ContractEvent(name = "issue-asset-2") | |||
| void issue(Bytes bytes, String assetHolderAddress, long cashNumber); | |||
| @ContractEvent(name = "issue-asset-3") | |||
| void issue(Byte byteObj, String assetHolderAddress, long cashNumber); | |||
| @ContractEvent(name = "issue-asset-4") | |||
| void issue(Byte byteObj, String assetHolderAddress, Bytes cashNumber); | |||
| } | |||
| @@ -0,0 +1,99 @@ | |||
| package com.jd.blockchain.contract; | |||
| import com.jd.blockchain.ledger.ContractBizContent; | |||
| import com.jd.blockchain.ledger.KVDataEntry; | |||
| import com.jd.blockchain.ledger.KVDataObject; | |||
| import com.jd.blockchain.utils.Bytes; | |||
| import java.util.Arrays; | |||
| /** | |||
| * 示例:一个“资产管理”智能合约的实现; | |||
| * | |||
| * 注: 1、实现 EventProcessingAwire 接口以便合约实例在运行时可以从上下文获得合约生命周期事件的通知; 2、实现 | |||
| * AssetContract 接口定义的合约方法; | |||
| * | |||
| * @author huanghaiquan | |||
| * | |||
| */ | |||
| @Contract | |||
| public class AssetContractImpl2 implements EventProcessingAwire, AssetContract2 { | |||
| public static String KEY_TOTAL = "total"; | |||
| // 合约事件上下文; | |||
| private ContractEventContext eventContext; | |||
| @Override | |||
| @ContractEvent(name = "issue-asset-0") | |||
| public void issue(ContractBizContent contractBizContent, String assetHolderAddress) { | |||
| System.out.println("input addr="+ Arrays.toString(contractBizContent.getAttrs())); | |||
| } | |||
| @Override | |||
| @ContractEvent(name = "issue-asset") | |||
| public void issue(ContractBizContent contractBizContent, String assetHolderAddress, long cashNumber) { | |||
| System.out.println("eventContext="+eventContext.getCurrentLedgerHash().toBase58()); | |||
| System.out.println("getAttrs: "+Arrays.toString(contractBizContent.getAttrs())+",address="+assetHolderAddress+",cashNumber="+cashNumber); | |||
| eventContext.getLedger().dataAccount(assetHolderAddress).set(contractBizContent.getAttrs()[0], "value1",-1); | |||
| eventContext.getLedger().dataAccount(assetHolderAddress).set(contractBizContent.getAttrs()[1], 888,-1); | |||
| } | |||
| @Override | |||
| @ContractEvent(name = "issue-asset-2") | |||
| public void issue(Bytes bytes, String assetHolderAddress, long cashNumber){ | |||
| System.out.println(String.format("bytes=%s,assetHolderAddress=%s,cashNumber=%d",new String(bytes.toBytes()),assetHolderAddress,cashNumber)); | |||
| } | |||
| @ContractEvent(name = "issue-asset-3") | |||
| @Override | |||
| public void issue(Byte byteObj, String assetHolderAddress, long cashNumber) { | |||
| System.out.println(String.format("issue(),bytes=%d,assetHolderAddress=%s,cashNumber=%d",byteObj.intValue(),assetHolderAddress,cashNumber)); | |||
| } | |||
| @ContractEvent(name = "issue-asset-4") | |||
| @Override | |||
| public void issue(Byte byteObj, String assetHolderAddress, Bytes cashNumber) { | |||
| System.out.println(String.format("issue(),bytes=%d,assetHolderAddress=%s,cashNumber=%s",byteObj.intValue(),assetHolderAddress,cashNumber.toString())); | |||
| System.out.println("current LedgerHash="+eventContext.getCurrentLedgerHash().toBase58()); | |||
| // 查询当前值; | |||
| KVDataEntry[] kvEntries = eventContext.getLedger().getDataEntries(eventContext.getCurrentLedgerHash(), assetHolderAddress, KEY_TOTAL); | |||
| // 计算资产的发行总数; | |||
| KVDataObject curTotal = (KVDataObject) kvEntries[0]; | |||
| System.out.println("currTotal version="+curTotal.getVersion()+",value="+curTotal.getValue().toString()); | |||
| eventContext.getLedger().dataAccount(assetHolderAddress).set(KEY_TOTAL, 100,curTotal.getVersion()); | |||
| } | |||
| /* | |||
| * (non-Javadoc) | |||
| * | |||
| * @see | |||
| * com.jd.blockchain.contract.model.EventProcessingAwire#beforeEvent(com.jd. | |||
| * blockchain.contract.model.ContractEventContext) | |||
| */ | |||
| @Override | |||
| public void beforeEvent(ContractEventContext eventContext) { | |||
| this.eventContext = eventContext; | |||
| } | |||
| /* | |||
| * (non-Javadoc) | |||
| * | |||
| * @see com.jd.blockchain.contract.model.EventProcessingAwire#postEvent(com.jd. | |||
| * blockchain.contract.model.ContractEventContext, | |||
| * com.jd.blockchain.contract.model.ContractError) | |||
| */ | |||
| @Override | |||
| public void postEvent(ContractEventContext eventContext, ContractException error) { | |||
| this.eventContext = null; | |||
| } | |||
| @Override | |||
| public void postEvent(ContractException error) { | |||
| } | |||
| @Override | |||
| public void postEvent() { | |||
| } | |||
| } | |||
| @@ -0,0 +1,24 @@ | |||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
| <modelVersion>4.0.0</modelVersion> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>project-to-test</artifactId> | |||
| <version>1.0-SNAPSHOT</version> | |||
| <packaging>jar</packaging> | |||
| <name>Test MyMojo</name> | |||
| <build> | |||
| <plugins> | |||
| <plugin> | |||
| <artifactId>contract-maven-plugin</artifactId> | |||
| <configuration> | |||
| <!--<outputDirectory>target/test-harness/project-to-test</outputDirectory>--> | |||
| <!-- The defined stubs --> | |||
| <project implementation="com.jd.blockchain.ledger.MyProjectStub"/> | |||
| <finalName>contract1</finalName> | |||
| </configuration> | |||
| </plugin> | |||
| </plugins> | |||
| </build> | |||
| </project> | |||
| @@ -0,0 +1,14 @@ | |||
| package com.jd.blockchain.ledger; | |||
| import com.jd.blockchain.binaryproto.DataContract; | |||
| import com.jd.blockchain.consts.DataCodes; | |||
| import java.lang.reflect.Method; | |||
| /** | |||
| * contract's args; | |||
| */ | |||
| @DataContract(code = DataCodes.CONTRACT_ARGS) | |||
| public interface ContractArgs { | |||
| Method getMethod(); | |||
| Object[] getArgs(); | |||
| } | |||
| @@ -32,10 +32,10 @@ public class DataAccountKVSetOperationBuilderImpl implements DataAccountKVSetOpe | |||
| public DataAccountKVSetOperationBuilder set(String key, String value, long expVersion) { | |||
| BytesValue bytesValue; | |||
| if (JSONSerializeUtils.isJSON(value)) { | |||
| bytesValue = new BytesValueEntry(BytesValueType.JSON, value.getBytes()); | |||
| bytesValue = new BytesValueEntry(BytesValueType.JSON, BytesUtils.toBytes(value)); | |||
| } | |||
| else { | |||
| bytesValue = new BytesValueEntry(BytesValueType.TEXT, value.getBytes()); | |||
| bytesValue = new BytesValueEntry(BytesValueType.TEXT, BytesUtils.toBytes(value)); | |||
| } | |||
| operation.set(key, bytesValue, expVersion); | |||
| return this; | |||
| @@ -18,10 +18,12 @@ import org.junit.Test; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import org.springframework.core.io.ClassPathResource; | |||
| import org.springframework.util.ReflectionUtils; | |||
| import java.io.File; | |||
| import java.io.FileInputStream; | |||
| import java.io.IOException; | |||
| import java.lang.reflect.Method; | |||
| import static org.junit.Assert.*; | |||
| @@ -351,4 +353,24 @@ public class SDK_Contract_Test { | |||
| ContractBizContent actualObj = BinaryProtocol.decodeAs(bizBytes,ContractBizContent.class); | |||
| assertArrayEquals(contractBizContent.getAttrs(),actualObj.getAttrs()); | |||
| } | |||
| @Test | |||
| public void testContractArgs(){ | |||
| ContractBizContent contractBizContent = () -> new String[]{"param1"}; | |||
| Method method = ReflectionUtils.findMethod(AssetContract2.class,"issue",ContractBizContent.class,String.class); | |||
| ContractArgs contractArgs = new ContractArgs() { | |||
| @Override | |||
| public Method getMethod() { | |||
| return method; | |||
| } | |||
| @Override | |||
| public Object[] getArgs() { | |||
| return new Object[]{contractBizContent,"hello"}; | |||
| } | |||
| }; | |||
| //add the annotation; | |||
| } | |||
| } | |||