Skip to content

Commit bbc5418

Browse files
committed
Merge pull request #79 from /issues/13
Issue #13: Implement signatures artifacts, downloaded from Maven repos
2 parents 6d441d9 + 4c4b4b2 commit bbc5418

File tree

5 files changed

+249
-21
lines changed

5 files changed

+249
-21
lines changed

ivy.xml

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
<!-- we compile against the minimum ANT / Maven / Gradle versions: -->
2727
<dependency org="org.apache.ant" name="ant" rev="1.7.0" conf="build"/>
2828
<dependency org="org.apache.maven" name="maven-plugin-api" rev="2.0" conf="build"/>
29+
<dependency org="org.apache.maven" name="maven-artifact" rev="2.0" conf="build"/>
2930
<dependency org="org.apache.maven.plugin-tools" name="maven-plugin-annotations" rev="3.2" conf="build"/>
3031
<dependency org="org.gradle" name="gradle-core" rev="2.3" conf="build"/>
3132
<dependency org="org.gradle" name="gradle-base-services" rev="2.3" conf="build"/>

src/main/java/de/thetaphi/forbiddenapis/maven/AbstractCheckMojo.java

+114-21
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,15 @@
1818

1919
import static de.thetaphi.forbiddenapis.Checker.Option.*;
2020

21+
import org.apache.maven.artifact.Artifact;
22+
import org.apache.maven.artifact.factory.ArtifactFactory;
23+
import org.apache.maven.artifact.repository.ArtifactRepository;
24+
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
25+
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
26+
import org.apache.maven.artifact.resolver.ArtifactResolver;
2127
import org.apache.maven.plugin.AbstractMojo;
2228
import org.apache.maven.plugin.MojoExecutionException;
23-
import org.apache.maven.plugin.logging.Log;
29+
import org.apache.maven.plugins.annotations.Component;
2430
import org.apache.maven.plugins.annotations.Parameter;
2531
import org.codehaus.plexus.util.DirectoryScanner;
2632

@@ -33,6 +39,8 @@
3339
import java.io.File;
3440
import java.io.IOException;
3541
import java.lang.annotation.RetentionPolicy;
42+
import java.net.URI;
43+
import java.net.URISyntaxException;
3644
import java.net.URLClassLoader;
3745
import java.net.MalformedURLException;
3846
import java.net.URL;
@@ -41,6 +49,7 @@
4149
import java.util.LinkedHashSet;
4250
import java.util.List;
4351
import java.util.Locale;
52+
import java.util.Set;
4453

4554
/**
4655
* Base class for forbiddenapis Mojos.
@@ -56,6 +65,36 @@ public abstract class AbstractCheckMojo extends AbstractMojo {
5665
@Parameter(required = false)
5766
private File[] signaturesFiles;
5867

68+
/**
69+
* Lists all Maven artifacts, which contain signatures and comments for forbidden API calls.
70+
* The artifact needs to be specified like a Maven dependency. Resolution is not transitive.
71+
* You can refer to plain text Maven artifacts ({@code type="txt"}, e.g., with a separate {@code classifier}):
72+
* <pre>
73+
* &lt;signaturesArtifact&gt;
74+
* &lt;groupId&gt;org.apache.foobar&lt;/groupId&gt;
75+
* &lt;artifactId&gt;example&lt;/artifactId&gt;
76+
* &lt;version&gt;1.0&lt;/version&gt;
77+
* &lt;classifier&gt;signatures&lt;/classifier&gt;
78+
* &lt;type&gt;txt&lt;/type&gt;
79+
* &lt;/signaturesArtifact&gt;
80+
* </pre>
81+
* Alternatively, refer to signatures files inside JAR artifacts. In that case, the additional
82+
* parameter {@code path} has to be given:
83+
* <pre>
84+
* &lt;signaturesArtifact&gt;
85+
* &lt;groupId&gt;org.apache.foobar&lt;/groupId&gt;
86+
* &lt;artifactId&gt;example&lt;/artifactId&gt;
87+
* &lt;version&gt;1.0&lt;/version&gt;
88+
* &lt;type&gt;jar&lt;/type&gt;
89+
* &lt;path&gt;path/inside/jar/file/signatures.txt&lt;/path&gt;
90+
* &lt;/signaturesArtifact&gt;
91+
* </pre>
92+
* <p>The signatures are resolved against the compile classpath.
93+
* @since 2.0
94+
*/
95+
@Parameter(required = false)
96+
private SignaturesArtifact[] signaturesArtifacts;
97+
5998
/**
6099
* Gives a multiline list of signatures, inline in the pom.xml. Use an XML CDATA section to do that!
61100
* The signatures are resolved against the compile classpath.
@@ -163,6 +202,18 @@ public abstract class AbstractCheckMojo extends AbstractMojo {
163202
/** The project packaging (pom, jar, etc.). */
164203
@Parameter(defaultValue = "${project.packaging}", readonly = true, required = true)
165204
private String packaging;
205+
206+
@Component
207+
private ArtifactFactory artifactFactory;
208+
209+
@Component
210+
private ArtifactResolver artifactResolver;
211+
212+
@Parameter(defaultValue = "${project.remoteArtifactRepositories}", readonly = true, required = true)
213+
private List<ArtifactRepository> remoteRepositories;
214+
215+
@Parameter(defaultValue = "${localRepository}", readonly = true, required = true)
216+
private ArtifactRepository localRepository;
166217

167218
/** provided by the concrete Mojos for compile and test classes processing */
168219
protected abstract List<String> getClassPathElements();
@@ -174,10 +225,46 @@ public abstract class AbstractCheckMojo extends AbstractMojo {
174225
protected String getTargetVersion() {
175226
return targetVersion;
176227
}
228+
229+
private File resolveSignaturesArtifact(SignaturesArtifact signaturesArtifact) throws ArtifactResolutionException, ArtifactNotFoundException {
230+
final Artifact artifact = signaturesArtifact.createArtifact(artifactFactory);
231+
artifactResolver.resolve(artifact, this.remoteRepositories, this.localRepository);
232+
return artifact.getFile();
233+
}
234+
235+
private String encodeUrlPath(String path) {
236+
try {
237+
// hack to encode the URL path by misusing URI class:
238+
return new URI(null, path, null).toASCIIString();
239+
} catch (URISyntaxException e) {
240+
throw new IllegalArgumentException(e.getMessage());
241+
}
242+
}
243+
244+
private URL createJarUrl(File f, String jarPath) throws MalformedURLException {
245+
final URL fileUrl = f.toURI().toURL();
246+
final URL jarBaseUrl = new URL("jar", null, fileUrl.toExternalForm() + "!/");
247+
return new URL(jarBaseUrl, encodeUrlPath(jarPath));
248+
}
177249

178250
@Override
179251
public void execute() throws MojoExecutionException {
180-
final Log log = getLog();
252+
final Logger log = new Logger() {
253+
@Override
254+
public void error(String msg) {
255+
getLog().error(msg);
256+
}
257+
258+
@Override
259+
public void warn(String msg) {
260+
getLog().warn(msg);
261+
}
262+
263+
@Override
264+
public void info(String msg) {
265+
getLog().info(msg);
266+
}
267+
};
181268

182269
if (skip) {
183270
log.info("Skipping forbidden-apis checks.");
@@ -202,7 +289,6 @@ public void execute() throws MojoExecutionException {
202289
urls[i++] = new File(cpElement).toURI().toURL();
203290
}
204291
assert i == urls.length;
205-
if (log.isDebugEnabled()) log.debug("Compile Classpath: " + Arrays.toString(urls));
206292
} catch (MalformedURLException e) {
207293
throw new MojoExecutionException("Failed to build classpath.", e);
208294
}
@@ -218,22 +304,7 @@ public void execute() throws MojoExecutionException {
218304
if (failOnMissingClasses) options.add(FAIL_ON_MISSING_CLASSES);
219305
if (failOnViolation) options.add(FAIL_ON_VIOLATION);
220306
if (failOnUnresolvableSignatures) options.add(FAIL_ON_UNRESOLVABLE_SIGNATURES);
221-
final Checker checker = new Checker(new Logger() {
222-
@Override
223-
public void error(String msg) {
224-
log.error(msg);
225-
}
226-
227-
@Override
228-
public void warn(String msg) {
229-
log.warn(msg);
230-
}
231-
232-
@Override
233-
public void info(String msg) {
234-
log.info(msg);
235-
}
236-
}, loader, options);
307+
final Checker checker = new Checker(log, loader, options);
237308

238309
if (!checker.isSupportedJDK) {
239310
final String msg = String.format(Locale.ENGLISH,
@@ -287,9 +358,27 @@ public void info(String msg) {
287358
checker.parseBundledSignatures(bs, targetVersion);
288359
}
289360
}
290-
if (signaturesFiles != null) for (final File f : new LinkedHashSet<File>(Arrays.asList(signaturesFiles))) {
361+
final Set<File> sigFiles = new LinkedHashSet<File>();
362+
final Set<URL> sigUrls = new LinkedHashSet<URL>();
363+
if (signaturesFiles != null) {
364+
sigFiles.addAll(Arrays.asList(signaturesFiles));
365+
}
366+
if (signaturesArtifacts != null) {
367+
for (final SignaturesArtifact artifact : signaturesArtifacts) {
368+
final File f = resolveSignaturesArtifact(artifact);
369+
if (artifact.path != null) {
370+
sigUrls.add(createJarUrl(f, artifact.path));
371+
} else {
372+
sigFiles.add(f);
373+
}
374+
}
375+
}
376+
for (final File f : sigFiles) {
291377
checker.parseSignaturesFile(f);
292378
}
379+
for (final URL u : sigUrls) {
380+
checker.parseSignaturesFile(u);
381+
}
293382
final String sig = (signatures != null) ? signatures.trim() : null;
294383
if (sig != null && sig.length() != 0) {
295384
checker.parseSignaturesString(sig);
@@ -298,11 +387,15 @@ public void info(String msg) {
298387
throw new MojoExecutionException("IO problem while reading files with API signatures.", ioe);
299388
} catch (ParseException pe) {
300389
throw new MojoExecutionException("Parsing signatures failed: " + pe.getMessage(), pe);
390+
} catch (ArtifactResolutionException e) {
391+
throw new MojoExecutionException("Problem while resolving Maven artifact.", e);
392+
} catch (ArtifactNotFoundException e) {
393+
throw new MojoExecutionException("Maven artifact does not exist.", e);
301394
}
302395

303396
if (checker.hasNoSignatures()) {
304397
if (failOnUnresolvableSignatures) {
305-
throw new MojoExecutionException("No API signatures found; use parameters 'signatures', 'bundledSignatures', and/or 'signaturesFiles' to define those!");
398+
throw new MojoExecutionException("No API signatures found; use parameters 'signatures', 'bundledSignatures', 'signaturesFiles', and/or 'signaturesArtifacts' to define those!");
306399
} else {
307400
log.info("Skipping execution because no API signatures are available.");
308401
return;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package de.thetaphi.forbiddenapis.maven;
2+
3+
import org.apache.maven.artifact.Artifact;
4+
import org.apache.maven.artifact.factory.ArtifactFactory;
5+
6+
/*
7+
* (C) Copyright Uwe Schindler (Generics Policeman) and others.
8+
*
9+
* Licensed under the Apache License, Version 2.0 (the "License");
10+
* you may not use this file except in compliance with the License.
11+
* You may obtain a copy of the License at
12+
*
13+
* http://www.apache.org/licenses/LICENSE-2.0
14+
*
15+
* Unless required by applicable law or agreed to in writing, software
16+
* distributed under the License is distributed on an "AS IS" BASIS,
17+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
* See the License for the specific language governing permissions and
19+
* limitations under the License.
20+
*/
21+
22+
/**
23+
* Defines coordinates of a Maven artifact that provides signatures files.
24+
* It may be a plain text file ({@link #path} is not given) or alternatively
25+
* refer to a text file inside a JAR file. For that, define the resource path
26+
* using {@link #path}.
27+
* @since 2.0
28+
*/
29+
public final class SignaturesArtifact {
30+
31+
/** Artifact's group ID (required) */
32+
public String groupId;
33+
34+
/** Artifact's ID (required) */
35+
public String artifactId;
36+
37+
/** Version (required) */
38+
public String version;
39+
40+
/** Classifier (optional) */
41+
public String classifier;
42+
43+
/**
44+
* Type (required; {@code txt} or {@code jar}).
45+
* If the artifact refers to a JAR file, the {@link #path} should be
46+
* given, that identifies the signatures file inside the JAR.
47+
* */
48+
public String type;
49+
50+
/** Path to resource inside JAR artifacts. If given, the {@link #type} must be {@code "jar"} or {@code "zip"}. */
51+
public String path;
52+
53+
/** Used by the mojo to fetch the artifact */
54+
Artifact createArtifact(ArtifactFactory artifactFactory) {
55+
if (groupId == null || artifactId == null || version == null || type == null) {
56+
throw new NullPointerException("signaturesArtifact is missing some properties. Required are: groupId, artifactId, version, type");
57+
}
58+
return artifactFactory.createArtifactWithClassifier(groupId, artifactId, version, type, classifier);
59+
}
60+
}

src/test/antunit/TestMavenMojo.xml

+18
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,22 @@
109109
<au:assertLogContains text="Reading bundled API signatures: jdk-unsafe"/>
110110
</target>
111111

112+
<target name="testSigArtifacts-Maven2">
113+
<artifact:mvn pom="pom-sigArtifacts.xml" mavenVersion="${maven.version}" failonerror="true" fork="${maven.fork}">
114+
<syspropertyset refid="injected-properties"/>
115+
<arg value="${groupId}:${artifactId}:${version}:check"/>
116+
</artifact:mvn>
117+
<au:assertLogContains text=" 0 error(s)."/>
118+
<au:assertLogContains text="Reading API signatures: jar:file:"/>
119+
</target>
120+
121+
<target name="testSigArtifacts-Maven3">
122+
<artifact:mvn pom="pom-sigArtifacts.xml" mavenVersion="${antunit.maven3.version}" failonerror="true" fork="${maven.fork}">
123+
<syspropertyset refid="injected-properties"/>
124+
<arg value="${groupId}:${artifactId}:${version}:check"/>
125+
</artifact:mvn>
126+
<au:assertLogContains text=" 0 error(s)."/>
127+
<au:assertLogContains text="Reading API signatures: jar:file:"/>
128+
</target>
129+
112130
</project>

src/test/antunit/pom-sigArtifacts.xml

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
* (C) Copyright Uwe Schindler (Generics Policeman) and others.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
-->
17+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
18+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
19+
<modelVersion>4.0.0</modelVersion>
20+
<groupId>de.thetaphi</groupId>
21+
<artifactId>forbiddenapi-test3</artifactId>
22+
<version>0.0-SNAPSHOT</version>
23+
<name>Dummy Project to test signatures from Maven artifacts</name>
24+
25+
<build>
26+
<plugins>
27+
<plugin>
28+
<groupId>${groupId}</groupId>
29+
<artifactId>${artifactId}</artifactId>
30+
<version>${version}</version>
31+
<configuration>
32+
<classesDirectory>.</classesDirectory>
33+
<internalRuntimeForbidden>false</internalRuntimeForbidden>
34+
<failOnMissingClasses>false</failOnMissingClasses>
35+
<signaturesArtifacts>
36+
<signaturesArtifact>
37+
<groupId>${groupId}</groupId>
38+
<artifactId>${artifactId}</artifactId>
39+
<version>${version}</version>
40+
<type>jar</type>
41+
<path>de/thetaphi/forbiddenapis/signatures/jdk-deprecated-${jdk.version}.txt</path>
42+
</signaturesArtifact>
43+
</signaturesArtifacts>
44+
</configuration>
45+
<executions>
46+
<execution>
47+
<goals>
48+
<goal>check</goal>
49+
</goals>
50+
</execution>
51+
</executions>
52+
</plugin>
53+
</plugins>
54+
</build>
55+
56+
</project>

0 commit comments

Comments
 (0)