Skip to content

Commit

Permalink
#274 the post analysis script can be loaded from the classpath
Browse files Browse the repository at this point in the history
  • Loading branch information
siom79 committed Jan 23, 2021
1 parent 1bf03f9 commit f0f8452
Show file tree
Hide file tree
Showing 8 changed files with 229 additions and 48 deletions.
54 changes: 7 additions & 47 deletions japicmp-maven-plugin/src/main/java/japicmp/maven/JApiCmpMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,11 @@
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.*;

import javax.script.Bindings;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
Expand Down Expand Up @@ -146,7 +143,8 @@ Optional<XmlOutput> executeWithParameters(PluginParameters pluginParameters, Mav
}
List<JApiClass> jApiClasses = jarArchiveComparator.compare(options.getOldArchives(), options.getNewArchives());
try {
jApiClasses = applyPostAnalysisScript(pluginParameters.getParameterParam(), jApiClasses);
PostAnalysisScriptExecutor postAnalysisScriptExecutor = new PostAnalysisScriptExecutor();
jApiClasses = postAnalysisScriptExecutor.apply(pluginParameters.getParameterParam(), jApiClasses, getLog());
File jApiCmpBuildDir = createJapiCmpBaseDir(pluginParameters);
generateDiffOutput(mavenParameters, pluginParameters, options, jApiClasses, jApiCmpBuildDir);
XmlOutput xmlOutput = generateXmlOutput(jApiClasses, jApiCmpBuildDir, options, mavenParameters, pluginParameters);
Expand Down Expand Up @@ -197,45 +195,7 @@ private void setUpOverrideCompatibilityChanges(JarArchiveComparatorOptions compa
}
}

private List<JApiClass> applyPostAnalysisScript(Parameter parameter, List<JApiClass> jApiClasses) throws MojoFailureException {
List<JApiClass> filteredList = jApiClasses;
if (parameter != null) {
String postAnalysisFilterScript = parameter.getPostAnalysisScript();
if (postAnalysisFilterScript != null) {
if (Files.exists(Paths.get(postAnalysisFilterScript))) {
ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName("groovy");
Bindings bindings = scriptEngine.createBindings();
bindings.put("jApiClasses", jApiClasses);
try (InputStreamReader fileReader = new InputStreamReader(new FileInputStream(postAnalysisFilterScript), Charset.forName("UTF-8"))) {
Object returnValue = scriptEngine.eval(fileReader, bindings);
if (returnValue instanceof List) {
List returnedList = (List) returnValue;
filteredList = new ArrayList<>(returnedList.size());
for (Object obj : returnedList) {
if (obj instanceof JApiClass) {
JApiClass jApiClass = (JApiClass) obj;
filteredList.add(jApiClass);
}
}
} else {
throw new MojoFailureException("Post-analysis script does not return a list.");
}
} catch (ScriptException e) {
throw new MojoFailureException("Execution of post-analysis script failed: " + e.getMessage(), e);
} catch (FileNotFoundException e) {
throw new MojoFailureException("Post-analysis script '" + postAnalysisFilterScript + " does not exist.", e);
} catch (IOException e) {
throw new MojoFailureException("Failed to load post-analysis script '" + postAnalysisFilterScript + ": " + e.getMessage(), e);
}
} else {
throw new MojoFailureException("Post-analysis script '" + postAnalysisFilterScript + " does not exist.");
}
} else {
getLog().debug("No post-analysis script provided.");
}
}
return filteredList;
}


private boolean skipModule(PluginParameters pluginParameters, MavenParameters mavenParameters) {
SkipModuleStrategy skipModuleStrategy = new SkipModuleStrategy(pluginParameters, mavenParameters, getLog());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package japicmp.maven;

import japicmp.model.JApiClass;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;

import javax.script.Bindings;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

public class PostAnalysisScriptExecutor {

public List<JApiClass> apply(Parameter parameter, List<JApiClass> jApiClasses, Log log) throws MojoFailureException {
List<JApiClass> filteredList = jApiClasses;
if (parameter != null) {
String postAnalysisFilterScript = parameter.getPostAnalysisScript();
if (postAnalysisFilterScript != null) {
try {
InputStream inputStream = getInputStream(postAnalysisFilterScript);
if (inputStream != null) {
ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName("groovy");
Bindings bindings = scriptEngine.createBindings();
bindings.put("jApiClasses", jApiClasses);
try (InputStreamReader fileReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
Object returnValue = scriptEngine.eval(fileReader, bindings);
if (returnValue instanceof List) {
List returnedList = (List) returnValue;
filteredList = new ArrayList<>(returnedList.size());
for (Object obj : returnedList) {
if (obj instanceof JApiClass) {
JApiClass jApiClass = (JApiClass) obj;
filteredList.add(jApiClass);
}
}
} else {
throw new MojoFailureException("Post-analysis script does not return a list.");
}
} catch (ScriptException e) {
throw new MojoFailureException("Execution of post-analysis script failed: " + e.getMessage(), e);
} catch (IOException e) {
throw new MojoFailureException("Failed to load post-analysis script '" + postAnalysisFilterScript + ": " + e.getMessage(), e);
}
} else {
throw new MojoFailureException("Post-analysis script '" + postAnalysisFilterScript + " does not exist.");
}
} catch (FileNotFoundException e) {
throw new MojoFailureException("Post-analysis script '" + postAnalysisFilterScript + " does not exist.", e);
}
} else {
log.debug("No post-analysis script provided.");
}
}
return filteredList;
}

private InputStream getInputStream(String postAnalysisFilterScript) throws FileNotFoundException {
if (Files.exists(Paths.get(postAnalysisFilterScript))) {
return new FileInputStream(postAnalysisFilterScript);
}
return PostAnalysisScriptExecutor.class.getClassLoader().getResourceAsStream(postAnalysisFilterScript);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<groupId>com.github.siom79.japicmp</groupId>
<artifactId>japicmp-testbase</artifactId>
<version>0.15.3-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>japicmp-test-maven-plugin-post-analysis-script-artifact-test</artifactId>

<dependencies>
<dependency>
<groupId>com.github.siom79.japicmp</groupId>
<artifactId>japicmp-test-maven-plugin-post-analysis-script-artifact</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>com.github.siom79.japicmp</groupId>
<artifactId>japicmp-maven-plugin</artifactId>
<version>${project.version}</version>
<configuration>
<oldVersion>
<dependency>
<groupId>com.github.siom79.japicmp</groupId>
<artifactId>japicmp-test-v1</artifactId>
<version>${project.version}</version>
</dependency>
</oldVersion>
<newVersion>
<dependency>
<groupId>com.github.siom79.japicmp</groupId>
<artifactId>japicmp-test-v1</artifactId>
<version>${project.version}</version>
</dependency>
</newVersion>
<parameter>
<!-- The post-analysis script is contained in "japicmp-test-maven-plugin-post-analysis-script-artifact" -->
<postAnalysisScript>post-analysis-script.groovy</postAnalysisScript>
</parameter>
</configuration>
<dependencies>
<dependency>
<groupId>com.github.siom79.japicmp</groupId>
<artifactId>japicmp-test-maven-plugin-post-analysis-script-artifact</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<phase>pre-integration-test</phase>
<goals>
<goal>cmp</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<phase>integration-test</phase>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package japicmp.test;

import org.junit.Assert;
import org.junit.Test;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

public class ITTestPostAnalysisScript {

@Test
public void testSkipViaUserProperty() throws IOException {
Path path = Paths.get(System.getProperty("user.dir"), "target", "japicmp", "japicmp.diff");
Assert.assertTrue(Files.exists(path));
List<String> lines = Files.readAllLines(path);
Assert.assertTrue(lines.size() > 0);
for (String line : lines) {
if (line.contains("japicmp.test.AbstractModifier")) {
Assert.fail("Diff file contains class that should have been filtered by groovy script from classpath");
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<groupId>com.github.siom79.japicmp</groupId>
<artifactId>japicmp-testbase</artifactId>
<version>0.15.3-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>japicmp-test-maven-plugin-post-analysis-script-artifact</artifactId>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
def it = jApiClasses.iterator()
while (it.hasNext()) {
def jApiClass = it.next()
def fqn = jApiClass.getFullyQualifiedName()
if (fqn == "japicmp.test.AbstractModifier") {
it.remove()
}
}
return jApiClasses
2 changes: 2 additions & 0 deletions japicmp-testbase/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
<module>japicmp-test-maven-plugin-packaging</module>
<module>japicmp-test-maven-plugin-packaging-bundle</module>
<module>japicmp-test-maven-plugin-userproperty</module>
<module>japicmp-test-maven-plugin-post-analysis-script-artifact</module>
<module>japicmp-test-maven-plugin-post-analysis-script-artifact-test</module>
<module>japicmp-test-ant-task</module>
</modules>

Expand Down
28 changes: 27 additions & 1 deletion src/site/markdown/MavenPlugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ the &lt;dependency&gt; element. Through the &lt;parameter&gt; element you can pr
| ignoreMissingOldVersion | true | false | If set to true, not resolvable artifacts for the old version do not break the build.|
| ignoreMissingNewVersion | true | false | If set to true, not resolvable artifacts for the new version do not break the build.|
| packagingSupported | true | n.a. | List all packaging type for which the plugin should be executed. Helpful if you define the plugin in a root pom.|
| postAnalysisScript | true | n.a. | A [Groovy](http|//www.groovy-lang.org/) script that gets invoked after analysis is completed and before the output is written. This way it can be used to filter the output or break the build on specific conditions.|
| postAnalysisScript | true | n.a. | A [Groovy](http|//www.groovy-lang.org/) script that gets invoked after analysis is completed and before the output is written. This way it can be used to filter the output or break the build on specific conditions. It can be an absolute path or a relative path of a file within the classpath.|
| skipXmlReport | true | false | If set to true, no XML report will be generated.|
| skipHtmlReport | true | false | If set to true, no HTML report will be generated.|
| skipDiffReport | true | false | If set to true, no diff report will be generated.|
Expand Down Expand Up @@ -445,3 +445,29 @@ while (it.hasNext()) {
}
return jApiClasses
```

The script can also be placed inside another artifact and loaded from the classpath. Therefore, add the artifact
containing the script as dependency to the japicmp-plugin:

```
<plugin>
<groupId>com.github.siom79.japicmp</groupId>
<artifactId>japicmp-maven-plugin</artifactId>
<version>${project.version}</version>
<configuration>
[...]
<parameter>
<!-- The post-analysis script is contained in "post-analysis-script-artifact" -->
<postAnalysisScript>post-analysis-script.groovy</postAnalysisScript>
</parameter>
</configuration>
<dependencies>
<dependency>
<groupId>com.github.siom79.japicmp</groupId>
<artifactId>post-analysis-script-artifact</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</plugin>
```

0 comments on commit f0f8452

Please sign in to comment.