From 20c7feccb8adb18ee0cacd872ad78bf63d4c15e0 Mon Sep 17 00:00:00 2001 From: zhaoguangwei Date: Thu, 27 Jun 2019 13:54:43 +0800 Subject: [PATCH] add the ContractCheckMojo for contract-maven-plugin; --- source/contract/contract-maven-plugin/pom.xml | 55 +---- .../com/jd/blockchain/ContractCheckMojo.java | 221 ++++++++++++++++++ .../test/resources/project-to-test/pom.xml | 2 +- .../project-to-test/target/contract.jar | Bin 8016 -> 5330 bytes 4 files changed, 229 insertions(+), 49 deletions(-) create mode 100644 source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/ContractCheckMojo.java diff --git a/source/contract/contract-maven-plugin/pom.xml b/source/contract/contract-maven-plugin/pom.xml index 75349275..7ba6a4f4 100644 --- a/source/contract/contract-maven-plugin/pom.xml +++ b/source/contract/contract-maven-plugin/pom.xml @@ -87,6 +87,13 @@ 3.6.0 provided + + + org.apache.maven.shared + maven-invoker + 3.0.1 + + @@ -110,31 +117,6 @@ false - - - - - - - - - - - - - - - - - - - - - - - - org.apache.maven.plugins maven-surefire-plugin @@ -142,29 +124,6 @@ true - - - org.apache.maven.plugins - maven-surefire-plugin - - true - - - - - - - - - - - - - - - - - diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/ContractCheckMojo.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/ContractCheckMojo.java new file mode 100644 index 00000000..a47e4eb6 --- /dev/null +++ b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/ContractCheckMojo.java @@ -0,0 +1,221 @@ +package com.jd.blockchain; + +import com.jd.blockchain.utils.ConsoleUtils; +import org.apache.maven.model.Model; +import org.apache.maven.model.Plugin; +import org.apache.maven.model.PluginExecution; +import org.apache.maven.model.io.xpp3.MavenXpp3Reader; +import org.apache.maven.model.io.xpp3.MavenXpp3Writer; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.MavenProject; +import org.apache.maven.shared.invoker.*; +import org.codehaus.plexus.util.xml.Xpp3Dom; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + + +@Mojo(name = "contractCheck") +public class ContractCheckMojo extends AbstractMojo { + Logger logger = LoggerFactory.getLogger(ContractCheckMojo.class); + + @Parameter(defaultValue = "${project}", required = true, readonly = true) + private MavenProject project; + + /** + * jar's name; + */ + @Parameter + private String finalName; + + + /** + * mainClass; + */ + @Parameter + private String mainClass; + /** + * ledgerVersion; + */ + @Parameter + private String ledgerVersion; + + /** + * mvnHome; + */ + @Parameter + private String mvnHome; + + /** + * first compile the class, then parse it; + */ + @Override + public void execute() { + this.compileFiles(); + + } + + private void compileFiles(){ + // 获取当前项目pom.xml文件所在路径 +// URL targetClasses = this.getClass().getClassLoader().getResource(""); +// File file = new File(targetClasses.getPath()); +// String pomXmlPath = file.getParentFile().getParent() + File.separator + "pom.xml"; + + FileInputStream fis = null; + try { +// fis = new FileInputStream(new File(pomXmlPath)); + fis = new FileInputStream(project.getFile()); + MavenXpp3Reader reader = new MavenXpp3Reader(); + Model model = reader.read(fis); + + //delete this plugin(contractCheck) from destination pom.xml;then add the proper plugins; + Plugin plugin = model.getBuild().getPluginsAsMap().get("com.jd.blockchain:contract-maven-plugin"); + if(plugin == null){ + plugin = model.getBuild().getPluginsAsMap().get("org.apache.maven.plugins:contract-maven-plugin"); + } + + if(plugin == null) { + return; + } + + model.getBuild().removePlugin(plugin); +// model.getBuild().setPlugins(null); + +// ConsoleUtils.info("----- 不携带Plugin -----"); +// print(model); + + List plugins = new ArrayList<>(); + plugins.add(createAssembly()); + plugins.add(createCheckImports()); + + model.getBuild().setPlugins(plugins); + + ConsoleUtils.info("----- add Plugin -----"); + handle(model); + + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void invokeCompile(File file) { + InvocationRequest request = new DefaultInvocationRequest(); + + Invoker invoker = new DefaultInvoker(); + try { + request.setPomFile(file); + + request.setGoals( Collections.singletonList( "verify" ) ); +// request.setMavenOpts("-DmainClass="+mainClass); + invoker.setMavenHome(new File(mvnHome)); + invoker.execute(request); + } catch (MavenInvocationException e) { + e.printStackTrace(); + } + } + + private Plugin createCheckImports() { + Plugin plugin = new Plugin(); + plugin.setGroupId("com.jd.blockchain"); + plugin.setArtifactId("contract-maven-plugin"); + plugin.setVersion(ledgerVersion); + + Xpp3Dom finalNameNode = new Xpp3Dom("finalName"); + finalNameNode.setValue(finalName); + Xpp3Dom configuration = new Xpp3Dom("configuration"); + configuration.addChild(finalNameNode); + plugin.setConfiguration(configuration); + + PluginExecution pluginExecution = new PluginExecution(); + pluginExecution.setId("make-assembly"); + pluginExecution.setPhase("verify"); + List goals = new ArrayList<>(); + goals.add("checkImports"); + pluginExecution.setGoals(goals); + List pluginExecutions = new ArrayList<>(); + pluginExecutions.add(pluginExecution); + plugin.setExecutions(pluginExecutions); + + return plugin; + } + + private Plugin createAssembly() { + Plugin plugin = new Plugin(); + plugin.setArtifactId("maven-assembly-plugin"); + + Xpp3Dom configuration = new Xpp3Dom("configuration"); + + Xpp3Dom mainClassNode = new Xpp3Dom("mainClass"); + mainClassNode.setValue(mainClass); + + Xpp3Dom manifest = new Xpp3Dom("manifest"); + manifest.addChild(mainClassNode); + + Xpp3Dom archive = new Xpp3Dom("archive"); + archive.addChild(manifest); + + Xpp3Dom finalNameNode = new Xpp3Dom("finalName"); + finalNameNode.setValue(finalName); + + Xpp3Dom appendAssemblyId = new Xpp3Dom("appendAssemblyId"); + appendAssemblyId.setValue("false"); + + Xpp3Dom descriptorRef = new Xpp3Dom("descriptorRef"); + descriptorRef.setValue("jar-with-dependencies"); + Xpp3Dom descriptorRefs = new Xpp3Dom("descriptorRefs"); + descriptorRefs.addChild(descriptorRef); + + configuration.addChild(finalNameNode); + configuration.addChild(appendAssemblyId); + configuration.addChild(archive); + configuration.addChild(descriptorRefs); + plugin.setConfiguration(configuration); + + PluginExecution pluginExecution = new PluginExecution(); + pluginExecution.setId("make-assembly"); + pluginExecution.setPhase("package"); + List goals = new ArrayList<>(); + goals.add("single"); + pluginExecution.setGoals(goals); + List pluginExecutions = new ArrayList<>(); + pluginExecutions.add(pluginExecution); + plugin.setExecutions(pluginExecutions); + return plugin; + } + + private void handle(Model model) throws IOException { + MavenXpp3Writer mavenXpp3Writer = new MavenXpp3Writer(); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + mavenXpp3Writer.write(outputStream, model); + + byte[] buffer = outputStream.toByteArray(); + + //输出文件 +// File fileOutput = new File("fileOut.xml"); +// File fileOutput = File.createTempFile("fileOut",".xml"); + File fileOutput = new File(project.getBasedir().getPath(),"fileOut.xml"); + fileOutput.createNewFile(); + + ConsoleUtils.info("fileOutput's path="+fileOutput.getPath()); + //创建文件输出流对象 + FileOutputStream fos = new FileOutputStream(fileOutput); + //将字节数组fileInput中的内容输出到文件fileOut.xml中; + ConsoleUtils.info(new String(buffer)); + fos.write(buffer); + invokeCompile(fileOutput); + fos.close(); + } +} diff --git a/source/contract/contract-maven-plugin/src/test/resources/project-to-test/pom.xml b/source/contract/contract-maven-plugin/src/test/resources/project-to-test/pom.xml index ea7962e8..80bf8e27 100644 --- a/source/contract/contract-maven-plugin/src/test/resources/project-to-test/pom.xml +++ b/source/contract/contract-maven-plugin/src/test/resources/project-to-test/pom.xml @@ -16,7 +16,7 @@ - contract1 + contract diff --git a/source/contract/contract-maven-plugin/src/test/resources/project-to-test/target/contract.jar b/source/contract/contract-maven-plugin/src/test/resources/project-to-test/target/contract.jar index 7c172d0edcb2a34e5aeecddac90a767afd4c204a..8e693437b273ffb193daabddc9ba9a683c034e26 100644 GIT binary patch literal 5330 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