开发者

Maven plugin to restrict specific packages from being used

开发者 https://www.devze.com 2023-04-05 14:46 出处:网络
I work in a team of around 40 developers, and I do not want any developer to use some specific API(java.sun.Base64 to be p开发者_JAVA技巧recise) to be used by any of the developers and rather make the

I work in a team of around 40 developers, and I do not want any developer to use some specific API(java.sun.Base64 to be p开发者_JAVA技巧recise) to be used by any of the developers and rather make them use alternatives to the sun API as its proprietary.

Are there any plugins for maven, by which , specifying the restricted packages in the pom.xml , the build will break if any of those packages are being used anywhere in the code??

Or is there a more graceful way to do this??

Thanks


You want to define an architectural rule for your project, which is best enforced by source code analysis.

Sonar now has the ability to specify such rules and display violations on the project's quality dashboard. If you want the build to break, this can additionally be done enabling Sonar's Build breaker plug-in.

Sonar is really easy to setup and integrates into your Maven build process with zero change to your POM.


Here is plug-in I wrote for similar purposes.

Details can be seen here: https://github.com/yamanyar/restrict-maven-plugin/wiki

Restrict all access from com.ya* to java.util.regex.*

<restriction>com.ya* to java.util.regex.*</restriction>

Restrict all access from com.ya* (except com.yamanyar.core.) to java.util.regex.,

<restriction>com.ya*,!com.yamanyar.core.* to java.util.regex.*</restriction>

Restrict all access from com.ya* (except com.yamanyar.core.) and com.abc.Test to java.util.regex.

<restriction>com.ya*,com.abc.Test,!com.yamanyar.core.* to java.util.regex.*</restriction>

Restrict all access from com.ya* (except com.yamanyar.core.) and com.abc.Test to java.util.regex. ( except java.util.regex.Matcher) <restriction>com.ya*,com.abc.Test,!com.yamanyar.core.* to java.util.regex.*,!java.util.regex.Matcher</restriction>

Restrict all access from com.ya* ( except com.yamanyar.core.) and com.abc.Test to java.util.regex. ( except java.util.regex.Matcher); and also restrict com.ya* (except com.yamanyar.core.) to java.io.PrintStre.print*()

<restriction>com.ya*,com.abc.Test,!com.yamanyar.core.* to java.util.regex.*,!java.util.regex.Matcher</restriction>
<restriction>com.ya*,!com.yamanyar.core* to java.io.PrintStre*.print*()</restriction>


Look at this:

<plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>macker-maven-plugin</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <executions>
          <execution>
            <phase>compile</phase>
            <goals>
              <goal>macker</goal>
            </goals>
          </execution>
        </executions>      
      </plugin>

where rule is defined not to allow import of java.lang.System

<?xml version="1.0"?>
<macker>    
    <ruleset name="Testing rules">
        <pattern name="mypackage" class="org.codehaus.mojo.**" />
        <access-rule>
            <message>System out is bad. Use logging instead.</message>
            <deny>
                <to>
                    <include class="java.lang.System" />
                </to>
            </deny>
            <!--allow>
                <from pattern="blah" />
            </allow-->
        </access-rule>
    </ruleset>
</macker>


I'm not aware of a Maven plugin to do this, but I would imagine you could do similar with aspects (and therefore use the Maven/Aspectj plugin). Aspectj has the declare error construct that may be useful. This can raise an error if it detects a pointcut that uses your forbidden classes.

Also http://www.eclipse.org/aspectj/doc/released/progguide/semantics-declare.html#warnings-and-errors

One limitation of this approach is it is a static analysis and so won't be able to catch any 'clever' invocations of your class/package black-list.


You could check which classes are loaded in your class loader and raise an error if you find something from java.sun.Base64.

This seems to work: http://www.javaworld.com/javaworld/javaqa/2003-07/02-qa-0725-classsrc2.html


Here is a proof of concept rule code for PMD/Maven PMD plugin. (The restricted classes are hard-coded in the constructor, but it's possible to make it configurable via properties.)

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import net.sourceforge.pmd.AbstractJavaRule;
import net.sourceforge.pmd.ast.ASTClassOrInterfaceType;
import net.sourceforge.pmd.ast.ASTName;
import net.sourceforge.pmd.ast.SimpleJavaNode;

public class PackageRestrictionRule extends AbstractJavaRule {

    private final List<String> disallowedPackages;

    public PackageRestrictionRule() {
        final List<String> disallowedPackages = new LinkedList<String>();
        disallowedPackages.add("org.apache.");
        this.disallowedPackages = Collections
                .unmodifiableList(disallowedPackages);
    }

    @Override
    public Object visit(final ASTClassOrInterfaceType node, 
            final Object data) {
        checkPackage(node, data);
        return super.visit(node, data);
    }

    @Override
    public Object visit(final ASTName node, final Object data) {
        checkPackage(node, data);
        return super.visit(node, data);
    }

    private void checkPackage(final SimpleJavaNode node, 
            final Object data) {
        final String image = node.getImage();
        if (isDisallowedPackage(image)) {
            addViolationWithMessage(data, node, 
                    "Disallowed class or package: " + image);
        }
    }

    private boolean isDisallowedPackage(final String packageName) {
        for (final String disallowedPackageName : disallowedPackages) {
            if (packageName.startsWith(disallowedPackageName)) {
                return true;
            }
        }
        return false;
    }
}

Create a new maven project for it, and use this project as a dependency of the PMD plugin in your project:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-pmd-plugin</artifactId>
    <version>2.5</version>
    <configuration>
        <targetJdk>1.6</targetJdk>
        <rulesets>
            <ruleset>packagerestrictionrule.xml</ruleset>
        </rulesets>
    </configuration>
    <dependencies>
        <dependency>
            <groupId>...</groupId>
            <artifactId>PackageRestrictionRule</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>
</plugin>

Furthermore, the PMD plugin needs a proper ruleset XML file for the rule class. There is an example on the PMD website: http://pmd.sourceforge.net/howtowritearule.html. Just place it to the src/main/resources folder in your PackageRestrictionRule project and the plugin will find it on the classpath.


This suggestion is the opposite of "graceful"; it is a total kluge: it might be simple enough to write something to put in the process-sources phase of the build ... you could (for example) replace any cases of "sun.Base64" with some (invalid Java) text indicating the problem. This would cause the build to fail at least.


One simple option can be to use a 'parent' pom to define all your third party jars with versions in 'Dependency Management' section and use them in child poms. Even though this model does not deny the usage of a particular jar, the PM or architect will have an easy way to manage the dependencies. Once this done, we can simply tell developers to use only the dependencies used in the parent pom.


I solved this problem in a easy way (might be not exactly what you are looking for but worked for us).
Basically create a test that inspect the source code and check if "something" is imported in some unwanted place.
You get a feedback faster than sonar and does not require any extra tools.

@Test
fun `MyRestrictedClass should be used only in package1 and package2`() {
    val otherFilesImportingMyRestrictedClass = File("src/main/java").walkTopDown().toList()
        .asSequence()
        .filter { it.isFile }
        .filter { it.readText(Charsets.UTF_8).contains("MyRestrictedClass") }
        .map { it.path }
        .filter { !it.contains("com/aa/package/of/my/restricted/class/MyRestrictedClass") }
        .filter { !it.contains("com/aa/allowed/package1") }
        .filter { !it.contains("com/aa/allowed/package2") }
        .toList()

    assertThat(otherFilesImportingMyRestrictedClass).isEmpty()
}

It's kotlin but I'm sure can be converted to java easily.
It has some imperfections and can throw some false positive but I'm sure you can deal with that when they occur.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号