...
 
Commits (2)
......@@ -4,11 +4,11 @@
<parent>
<artifactId>access-modifier</artifactId>
<groupId>org.kohsuke</groupId>
<version>1.12</version>
<version>1.14</version>
</parent>
<artifactId>access-modifier-annotation</artifactId>
<name>Custom Acess Modifier annotations</name>
<name>Custom Access Modifier annotations</name>
<dependencies>
<dependency>
......@@ -28,4 +28,4 @@
<scope>test</scope>
</dependency>
</dependencies>
</project>
\ No newline at end of file
</project>
......@@ -85,6 +85,13 @@ public abstract class AccessRestriction {
*/
public abstract void written(Location loc, RestrictedElement target, ErrorListener errorListener);
/**
* Whether this access restriction, if applied to a type, should also be considered to apply implicitly to all transitively nested members.
* @return by default, false
*/
public boolean appliesToNested() {
return false;
}
/**
* {@link AccessRestriction} that imposes no restriction.
......
......@@ -74,4 +74,10 @@ public interface Location {
* access restrictions.
*/
ClassLoader getDependencyClassLoader();
/**
* Loads a configuration setting from the environment, such as when configured by a Maven plugin.
*/
/*@CheckForNull*/ String getProperty(String key);
}
/*
* The MIT License
*
* Copyright 2018 CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.kohsuke.accmod.restrictions;
import org.kohsuke.accmod.impl.ErrorListener;
import org.kohsuke.accmod.impl.Location;
import org.kohsuke.accmod.impl.RestrictedElement;
/**
* References are only allowed within the same module, as in {@link NoExternalUse},
* or when a special flag is set in the consuming module.
* This is the property {@code useBeta} with the value {@code true}.
*/
public class Beta extends DoNotUse {
@Override
public void error(Location loc, RestrictedElement target, ErrorListener errorListener) {
if (target.isInTheInspectedModule()) {
return;
}
if ("true".equals(loc.getProperty("useBeta"))) {
return;
}
errorListener.onError(null, loc, target + " is still in beta");
}
}
......@@ -61,4 +61,10 @@ public class DoNotUse extends AccessRestriction {
public void error(Location loc, RestrictedElement target, ErrorListener errorListener) {
errorListener.onError(null,loc,target+" must not be used");
}
@Override
public boolean appliesToNested() {
return true;
}
}
......@@ -4,7 +4,7 @@
<parent>
<artifactId>access-modifier</artifactId>
<groupId>org.kohsuke</groupId>
<version>1.12</version>
<version>1.14</version>
</parent>
<artifactId>access-modifier-checker</artifactId>
<packaging>maven-plugin</packaging>
......
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>test</groupId>
<artifactId>beta-fail</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>api</artifactId>
<dependencies>
<dependency>
<groupId>org.kohsuke</groupId>
<artifactId>access-modifier-annotation</artifactId>
<version>@project.version@</version>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package api;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.Beta;
public class Api {
@Restricted(Beta.class)
public static void experimental() {}
static {
experimental(); // OK
}
}
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>test</groupId>
<artifactId>beta-fail</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>caller</artifactId>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.kohsuke</groupId>
<artifactId>access-modifier-checker</artifactId>
<version>@project.version@</version>
<executions>
<execution>
<goals>
<goal>enforce</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package caller;
import api.Api;
public class Caller {
public Caller() {
Api.experimental(); // illegal
}
}
invoker.goals=clean package
invoker.buildResult = failure
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>beta-fail</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<modules>
<module>api</module>
<module>caller</module>
</modules>
</project>
\ No newline at end of file
assert new File(basedir, 'build.log').text.contains('[ERROR] caller/Caller:8 api/Api.experimental()V is still in beta')
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>test</groupId>
<artifactId>beta-pass</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>api</artifactId>
<dependencies>
<dependency>
<groupId>org.kohsuke</groupId>
<artifactId>access-modifier-annotation</artifactId>
<version>@project.version@</version>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package api;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.Beta;
public class Api {
@Restricted(Beta.class)
public static void experimental() {}
static {
experimental(); // OK
}
}
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>test</groupId>
<artifactId>beta-pass</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>caller</artifactId>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.kohsuke</groupId>
<artifactId>access-modifier-checker</artifactId>
<version>@project.version@</version>
<executions>
<execution>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<properties>
<property>
<name>useBeta</name>
<value>true</value>
</property>
</properties>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package caller;
import api.Api;
public class Caller {
public Caller() {
Api.experimental(); // OK
}
}
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>beta-pass</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<modules>
<module>api</module>
<module>caller</module>
</modules>
</project>
\ No newline at end of file
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>test</groupId>
<artifactId>failOnError</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>api</artifactId>
<dependencies>
<dependency>
<groupId>org.kohsuke</groupId>
<artifactId>access-modifier-annotation</artifactId>
<version>@project.version@</version>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package api;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
public class Api {
@Restricted(NoExternalUse.class)
public static void notReallyPublic() {}
static {
notReallyPublic(); // OK
}
}
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>test</groupId>
<artifactId>failOnError</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>caller</artifactId>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<properties>
<access-modifier-checker.failOnError>false</access-modifier-checker.failOnError>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.kohsuke</groupId>
<artifactId>access-modifier-checker</artifactId>
<version>@project.version@</version>
<executions>
<execution>
<goals>
<goal>enforce</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package caller;
import api.Api;
public class Caller {
public Caller() {
Api.notReallyPublic(); // illegal
}
}
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>failOnError</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<modules>
<module>api</module>
<module>caller</module>
</modules>
</project>
\ No newline at end of file
assert new File(basedir, 'build.log').text.contains('[WARNING] caller/Caller:8 api/Api.notReallyPublic()V must not be used')
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>test</groupId>
<artifactId>method-inside-type</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>api</artifactId>
<dependencies>
<dependency>
<groupId>org.kohsuke</groupId>
<artifactId>access-modifier-annotation</artifactId>
<version>@project.version@</version>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package api;
import api.impl.NotApi;
public class Api {
public Api() {}
public static void actuallyPublic() {
NotApi.notReallyPublic(); // OK
new NotApi().notReallyPublicF.hashCode(); // OK
NotApi.AlsoNotApi.alsoNotReallyPublic(); // OK
}
public final Object actuallyPublicF = new Object();
}
package api.impl;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
@Restricted(NoExternalUse.class)
public class NotApi {
public NotApi() {}
public static void notReallyPublic() {}
public final Object notReallyPublicF = new Object();
static {
notReallyPublic(); // OK
new NotApi().notReallyPublicF.hashCode(); // OK
AlsoNotApi.alsoNotReallyPublic(); // OK
}
public static final class AlsoNotApi {
public static void alsoNotReallyPublic() {}
}
}
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>test</groupId>
<artifactId>method-inside-type</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>caller</artifactId>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.kohsuke</groupId>
<artifactId>access-modifier-checker</artifactId>
<version>@project.version@</version>
<executions>
<execution>
<goals>
<goal>enforce</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package caller;
import api.Api;
import api.impl.NotApi;
public class Caller {
public Caller() {
Api.actuallyPublic(); // OK
new Api().actuallyPublicF.hashCode(); // OK
NotApi.notReallyPublic(); // illegal
NotApi na = new NotApi(); // illegal
na.notReallyPublicF.hashCode(); // illegal
NotApi.AlsoNotApi.alsoNotReallyPublic(); // illegal
}
}
invoker.goals=clean package
invoker.buildResult = failure
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>method-inside-type</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<modules>
<module>api</module>
<module>caller</module>
</modules>
</project>
\ No newline at end of file
def text = new File(basedir, 'build.log').text
assert text.contains('[ERROR] caller/Caller:11 api/impl/NotApi must not be used')
assert text.contains('[ERROR] caller/Caller:12 api/impl/NotApi must not be used')
assert text.contains('[ERROR] caller/Caller:13 api/impl/NotApi must not be used')
assert text.contains('[ERROR] caller/Caller:14 api/impl/NotApi must not be used')
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>test</groupId>
<artifactId>skip</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>api</artifactId>
<dependencies>
<dependency>
<groupId>org.kohsuke</groupId>
<artifactId>access-modifier-annotation</artifactId>
<version>@project.version@</version>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package api;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
public class Api {
@Restricted(NoExternalUse.class)
public static void notReallyPublic() {}
static {
notReallyPublic(); // OK
}
}
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>test</groupId>
<artifactId>skip</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>caller</artifactId>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.kohsuke</groupId>
<artifactId>access-modifier-checker</artifactId>
<version>@project.version@</version>
<executions>
<execution>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<skip>true</skip>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package caller;
import api.Api;
public class Caller {
public Caller() {
Api.notReallyPublic(); // illegal
}
}
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>skip</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<modules>
<module>api</module>
<module>caller</module>
</modules>
</project>
\ No newline at end of file
assert new File(basedir, 'build.log').text.contains('[INFO] Skipping access modifier checks')
......@@ -42,9 +42,13 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import static org.objectweb.asm.ClassReader.SKIP_FRAMES;
......@@ -65,6 +69,8 @@ public class Checker {
*/
private final ErrorListener errorListener;
private final Properties properties;
/**
* Restrictions found from dependencies.
* <p>
......@@ -80,9 +86,10 @@ public class Checker {
private final AccessRestrictionFactory factory;
public Checker(ClassLoader dependencies, ErrorListener errorListener) throws IOException {
Checker(ClassLoader dependencies, ErrorListener errorListener, Properties properties) throws IOException {
this.dependencies = dependencies;
this.errorListener = errorListener;
this.properties = properties;
this.factory = new AccessRestrictionFactory(dependencies);
// load access restrictions
......@@ -230,12 +237,18 @@ public class Checker {
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
this.className = name;
if (superName!=null)
getRestrictions(superName).usedAsSuperType(currentLocation,errorListener);
if (superName != null) {
for (Restrictions r : getRestrictions(superName)) {
r.usedAsSuperType(currentLocation, errorListener);
}
}
if (interfaces!=null) {
for (String intf : interfaces)
getRestrictions(intf).usedAsInterface(currentLocation,errorListener);
if (interfaces != null) {
for (String intf : interfaces) {
for (Restrictions r : getRestrictions(intf)) {
r.usedAsInterface(currentLocation, errorListener);
}
}
}
}
......@@ -252,26 +265,34 @@ public class Checker {
public void visitTypeInsn(int opcode, String type) {
switch (opcode) {
case Opcodes.NEW:
getRestrictions(type).instantiated(currentLocation,errorListener);
for (Restrictions r : getRestrictions(type)) {
r.instantiated(currentLocation, errorListener);
}
}
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
getRestrictions(owner+'.'+name+desc).invoked(currentLocation,errorListener);
for (Restrictions r : getRestrictions(owner + '.' + name + desc)) {
r.invoked(currentLocation, errorListener);
}
}
@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
Restrictions r = getRestrictions(owner + '.' + name);
Iterable<Restrictions> rs = getRestrictions(owner + '.' + name);
switch (opcode) {
case Opcodes.GETSTATIC:
case Opcodes.GETFIELD:
r.read(currentLocation,errorListener);
for (Restrictions r : rs) {
r.read(currentLocation, errorListener);
}
break;
case Opcodes.PUTSTATIC:
case Opcodes.PUTFIELD:
r.written(currentLocation,errorListener);
for (Restrictions r : rs) {
r.written(currentLocation, errorListener);
}
break;
}
super.visitFieldInsn(opcode, owner, name, desc);
......@@ -308,9 +329,9 @@ public class Checker {
return dependencies;
}
public boolean isInTheSameModuleAs(RestrictedElement e) {
// TODO
throw new UnsupportedOperationException();
@Override
public String getProperty(String key) {
return properties.getProperty(key);
}
};
}, SKIP_FRAMES);
......@@ -319,10 +340,37 @@ public class Checker {
}
}
private Restrictions getRestrictions(String keyName) {
private Iterable<Restrictions> getRestrictions(String keyName) {
List<Restrictions> rs = new ArrayList<>();
Restrictions r = restrictions.get(keyName);
if (r==null) return Restrictions.NONE;
return r;
if (r != null) {
rs.add(r);
}
int idx = Integer.MAX_VALUE;
while (true) {
int newIdx = keyName.lastIndexOf('.', idx);
if (newIdx == -1) {
newIdx = keyName.lastIndexOf('$', idx);
if (newIdx == -1) {
break;
}
}
idx = newIdx;
keyName = keyName.substring(0, idx);
r = restrictions.get(keyName);
if (r != null) {
Collection<AccessRestriction> applicable = new ArrayList<>();
for (AccessRestriction ar : r) {
if (ar.appliesToNested()) {
applicable.add(ar);
}
}
if (!applicable.isEmpty()) {
rs.add(new Restrictions(r.target, applicable));
}
}
}
return rs;
}
private static final String RESTRICTED_DESCRIPTOR = Type.getDescriptor(Restricted.class);
......
......@@ -15,6 +15,7 @@ import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
/**
* Enforces the {@link Restricted} access modifier annotations.
......@@ -35,7 +36,29 @@ public class EnforcerMojo extends AbstractMojo {
*/
protected MavenProject project;
/**
* If true, skip running the checker entirely.
* @parameter expression="${access-modifier-checker.skip}" default-value="false"
*/
private boolean skip;
/**
* If false, print warnings about violations but do not fail the build.
* @parameter expression="${access-modifier-checker.failOnError}" default-value="true"
*/
private boolean failOnError = true;
/**
* Optional properties to also make available to restriction checkers.
* @parameter
*/
private Properties properties;
public void execute() throws MojoExecutionException, MojoFailureException {
if (skip) {
getLog().info("Skipping access modifier checks");
return;
}
try {
File outputDir = new File(project.getBuild().getOutputDirectory());
......@@ -49,14 +72,19 @@ public class EnforcerMojo extends AbstractMojo {
Checker checker = new Checker(new URLClassLoader(dependencies.toArray(new URL[dependencies.size()]), getClass().getClassLoader()),
new ErrorListener() {
public void onError(Throwable t, Location loc, String msg) {
getLog().error(loc+" "+msg,t);
String locMsg = loc+" "+msg;
if (failOnError) {
getLog().error(locMsg, t);
} else {
getLog().warn(locMsg, t);
}
failed[0] = true;
}
public void onWarning(Throwable t, Location loc, String msg) {
getLog().warn(loc+" "+msg,t);
}
});
}, properties != null ? properties : new Properties());
{// if there's restriction list in the inspected module itself, load it as well
InputStream self = null;
......@@ -70,8 +98,14 @@ public class EnforcerMojo extends AbstractMojo {
// perform checks
checker.check(outputDir);
if (failed[0])
throw new MojoFailureException("Access modifier checks failed. See the details above");
if (failed[0]) {
String message = "Access modifier checks failed. See the details above";
if (failOnError) {
throw new MojoFailureException(message);
} else {
getLog().warn(message);
}
}
} catch (IOException e) {
throw new MojoExecutionException("Failed to enforce @Restricted constraints",e);
}
......
......@@ -36,7 +36,7 @@ import java.util.List;
* @author Kohsuke Kawaguchi
*/
public class Restrictions extends ArrayList<AccessRestriction> {
private final RestrictedElement target;
final RestrictedElement target;
public Restrictions(RestrictedElement target, Collection<? extends AccessRestriction> c) {
super(c);
......@@ -77,13 +77,6 @@ public class Restrictions extends ArrayList<AccessRestriction> {
ar.written(location,target,errorListener);
}
public static final Restrictions NONE = new Restrictions(new RestrictedElement() {
public boolean isInTheInspectedModule() { return false; }
public String toString() { return "NONE"; }
});
abstract static class Parser extends AnnotationVisitor {
private List<Type> restrictions = new ArrayList<Type>();
private final RestrictedElement target;
......
......@@ -4,11 +4,12 @@
<parent>
<groupId>org.kohsuke</groupId>
<artifactId>pom</artifactId>
<version>17</version>
<version>19</version>
<relativePath />
</parent>
<artifactId>access-modifier</artifactId>
<version>1.12</version>
<version>1.14</version>
<name>Custom access modifier for Java</name>
<packaging>pom</packaging>
......@@ -30,7 +31,7 @@
<connection>scm:git:git@github.com/kohsuke/${project.artifactId}.git</connection>
<developerConnection>scm:git:ssh://git@github.com/kohsuke/${project.artifactId}.git</developerConnection>
<url>http://${project.artifactId}.kohsuke.org/</url>
<tag>access-modifier-1.12</tag>
<tag>access-modifier-1.14</tag>
</scm>
<developers>
......