Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/skuzzle/restrict-imports-enforcer-rule
Gradle plugin & Maven Enforcer rule that restricts usage of unwanted imports in Java, Kotlin and Groovy source files.
https://github.com/skuzzle/restrict-imports-enforcer-rule
ban enforce enforcer-rule import maven restrict
Last synced: 1 day ago
JSON representation
Gradle plugin & Maven Enforcer rule that restricts usage of unwanted imports in Java, Kotlin and Groovy source files.
- Host: GitHub
- URL: https://github.com/skuzzle/restrict-imports-enforcer-rule
- Owner: skuzzle
- License: mit
- Created: 2015-12-11T20:14:20.000Z (about 9 years ago)
- Default Branch: master
- Last Pushed: 2024-10-22T20:08:05.000Z (3 months ago)
- Last Synced: 2024-10-23T09:23:14.463Z (3 months ago)
- Topics: ban, enforce, enforcer-rule, import, maven, restrict
- Language: Java
- Homepage:
- Size: 1.2 MB
- Stars: 73
- Watchers: 4
- Forks: 12
- Open Issues: 13
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG_LEGACY.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
[![Maven Central](https://img.shields.io/static/v1?label=MavenCentral&message=2.6.0&color=blue)](https://search.maven.org/artifact/de.skuzzle.enforcer/restrict-imports-enforcer-rule/2.6.0/jar)
[![Gradle Plugin Portal](https://img.shields.io/gradle-plugin-portal/v/de.skuzzle.restrictimports?versionSuffix=2.6.0)](https://plugins.gradle.org/plugin/de.skuzzle.restrictimports/2.6.0)
[![Coverage Status](https://coveralls.io/repos/github/skuzzle/restrict-imports-enforcer-rule/badge.svg?branch=master)](https://coveralls.io/github/skuzzle/restrict-imports-enforcer-rule?branch=master)
[![Twitter Follow](https://img.shields.io/twitter/follow/skuzzleOSS.svg?style=social)](https://twitter.com/ProjectPolly)# restrict-imports-enforcer-rule
Keep your code base clean and free from usage of unwanted classes! [More](#rationale)**NEW** in 2.5.0: We now also provide a Gradle plugin!
Supported source files:
- [x] Java
- [x] Kotlin (since 0.15)
- [x] Groovy (since 0.15)Compatibility:
- Works with Java 8+
- Tested against _maven-enforcer-plugin_ versions `1.4.1` and `3.5.0`.## Maven quick start
This is a minimal usage example. Please scroll down for detailed configuration
information or have a look at the [Full configuration example](#full-configuration-example).```xml
org.apache.maven.plugins
maven-enforcer-plugin
3.5.0
de.skuzzle.enforcer
restrict-imports-enforcer-rule
2.6.0
check-logging-imports
process-sources
enforce
Use SLF4j for logging
java.util.logging.**
```
## Gradle quick start
> [!CAUTION]
> Gradle support is quite new and should be considered experimental.
>
> Feedback is welcome and should be filed as new GitHub issue.### ... with Groovy DSL
```
plugins {
id("de.skuzzle.restrictimports") version("2.6.0")
}restrictImports {
reason = "Use slf4j for logging"
bannedImports = ["java.util.logging.**"]
}
```### ... with Kotlin DSL
```
plugins {
id("de.skuzzle.restrictimports") version("2.6.0")
}restrictImports {
reason.set("Use slf4j for logging")
bannedImports.set(listOf("java.util.logging.**"))
}
```# Contents
* [Rationale](#rationale)
* Usage
* [Includes and Excludes](#includes-and-excludes)
* [Not fixable imports](#not-fixable-imports)
* [Rule groups](#rule-groups)
* [Static imports](#static-imports)
* [Test code](#test-code)
* [Skipping](#skipping)
* [Exclude source roots](#exclude-source-roots)
* [Parallel analysis](#parallel-analysis)
* [Detecting full qualified class usage](#detecting-full-qualified-class-usage)
* [Package patterns](#package-patterns)
* [Limitation](#limitation)
* [Syntactical](#syntactical-limitation)
* [Conceptual](#conceptual-limitation)
* [Full configuration example](#full-configuration-example)
* [Configuration options](#configuration-options)
* [Versioning, Deprecations and Compatibility](#versioning-deprecations-and-compatibility)## Rationale
Grown code bases often have a huge number of dependencies. That leads to a lot of clutter in their
compile time classpath. My favorite example here is logging frameworks: every java project
of decent size likely has numerous classes named `Logger` available on the classpath and your
favorite IDE happily lists them all for auto completion. How should someone new to
the project know which `Logger` to use? You certainly do not want to mix logging frameworks in your
code base.Another example is to force your developers to only use AssertJ assertions instead of JUnit or TestNG
assertions.Using this enforcer rule gives you fine grained control over which classes are allowed to be used in your
application without having to exclude whole artifacts from your classpath.## Includes and Excludes
To refine the classes that are banned you may use the `allowedImports` tag in addition to
the `bannedImports` tag. For example, you can exclude a whole sub package using a wildcard
operator but still allow some concrete classes:Maven
```xml
java.util.logging.**
java.util.logging.Handler
```
Gradle (Kotlin)
```kotlin
restrictImports {
bannedImports = listOf("java.util.logging.**")
allowedImports = listOf("java.util.logging.Handler")
}
```Gradle (Groovy)
```groovy
restrictImports {
bannedImports = ["java.util.logging.**"]
allowedImports = ["java.util.logging.Handler"]
}
```It is possible to exclude certain source files from being affected by the bans at
all. You can use `basePackage` to specify a package pattern of classes that are affected
by the rule. You may then exclude some classes to refine the matches using the
`exclusion` tag. It is also possible to specify multiple base packages.Maven
```xml
com.your.domain.**
com.your.company.**
java.util.logging.**
java.util.logging.Handler
com.your.domain.treat.special.*
```
Gradle (Kotlin)
```kotlin
restrictImports {
basePackes = listOf("com.your.domain.**", "com.your.company.**")
bannedImports = listOf("java.util.logging.**")
allowedImports = listOf("java.util.logging.Handler")
exclusions = listOf("com.your.domain.treat.special.*")
}
```Gradle (Groovy)
```groovy
restrictImports {
basePackes = ["com.your.domain.**", "com.your.company.**"]
bannedImports = ["java.util.logging.**"]
allowedImports = ["java.util.logging.Handler"]
exclusions = ["com.your.domain.treat.special.*"]
}
```Wherever you write package patterns you can also specify a list of patterns. Thus it is
possible to define multiple banned imports/exclusions/allowed imports or base packages.Maven
```xml
java.util.logging.**
what.ever.**
java.util.logging.Handler
what.ever.IsCool
com.your.domain.treat.special.*
com.your.domain.treat.special.too.*
```
Gradle (Kotlin)
```kotlin
restrictImports {
bannedImports = listOf("java.util.logging.**", "what.ever.**")
allowedImports = listOf("java.util.logging.Handler", "what.ever.IsCool")
exclusions = listOf("com.your.domain.treat.special.*", "com.your.domain.treat.special.too.*")
}
```Gradle (Groovy)
```groovy
restrictImports {
bannedImports = ["java.util.logging.**", "what.ever.**"]
allowedImports = ["java.util.logging.Handler", "what.ever.IsCool"]
exclusions = ["com.your.domain.treat.special.*", "com.your.domain.treat.special.too.*"]
}
```## Not-fixable imports
> [!NOTE]
> This is an experimental feature added in 2.4.0In certain situations you might not be able to avoid using a banned import. For example if you implement an
interface which requires a banned type as either return- or parameter type. Instead of globally allowing such imports,
you can allow them to be used only in some explicitly configured locations.Maven
You can add multiple _not-fixable_ definitions if you nest them in ``.
```xml
com.foo.BannedClass
com.yourdomain.persistence.SomeClass
com.foo.BannedClass
Type required by implemented interface
```
Gradle (Kotlin)
```kotlin
restrictImports {
bannedImports = listOf("com.foo.BannedClass")
notFixable {
in = "com.yourdomain.persistence.SomeClass"
allowedImports = listOf("com.foo.BannedClass")
because = "Type required by implemented interface"
}
}
```Gradle (Groovy)
```kotlin
restrictImports {
bannedImports = ["com.foo.BannedClass"]
notFixable {
in = "com.yourdomain.persistence.SomeClass"
allowedImports = ["com.foo.BannedClass"]
because = "Type required by implemented interface"
}
}
```> [!NOTE]
> Not fixable definitions can not be nested in `` (see _Rule groups_ below). Not-fixables apply globally per
> `RestrictImports` rule instance.## Rule groups
Rule groups add another level of refining which imports will be matched. You can group
the `bannedImport(s)`, `allowedImport(s)` and `basePackage(s)` tags and specify multiple
of this groups within a single enforcer rule.Maven
```xml
Persistence classes must only be used from within .persistence package
**
javax.persistence.EntityManager
javax.sql.DataSource
javax.persistence.NamedQueries
javax.persistence.NamedQuery
javax.ejb.Stateful
javax.ejb.EJB
com.yourdomain.persistence.**
javax.persistence.NamedQueries
javax.persistence.NamedQuery
javax.ejb.Stateful
javax.ejb.EJB
```
Gradle (Kotlin)
```kotlin
restrictImports {
group {
reason = "Persistence classes must only be used from within .persistence package"
basePackages = listOf("**")
bannedImports = listOf(
"javax.persistence.EntityManager",
"javax.sql.DataSource",
"javax.persistence.NamedQueries",
"javax.persistence.NamedQuery",
"javax.ejb.Stateful",
"javax.ejb.EJB"
)
}
group {
basePackages = listOf("com.yourdomain.persistence.**")
bannedImports = listOf(
"javax.persistence.NamedQueries",
"javax.persistence.NamedQuery",
"javax.ejb.Stateful",
"javax.ejb.EJB"
)
}
}
```Gradle (Groovy)
```groovy
restrictImports {
group {
reason = "Persistence classes must only be used from within .persistence package"
basePackages = ["**"]
bannedImports = [
"javax.persistence.EntityManager",
"javax.sql.DataSource",
"javax.persistence.NamedQueries",
"javax.persistence.NamedQuery",
"javax.ejb.Stateful",
"javax.ejb.EJB"
]
}
group {
basePackages = ["com.yourdomain.persistence.**"]
bannedImports = [
"javax.persistence.NamedQueries",
"javax.persistence.NamedQuery",
"javax.ejb.Stateful",
"javax.ejb.EJB"
]
}
}
```When analysing a source file, the plugin collects all groups where the group's
`basePackage` matches the source file's package name. In case multiple groups are
matching, only the group with the _most specific_ base package is retained and the others
are ignored for this file. Have a look at [this](https://github.com/skuzzle/restrict-imports-enforcer-rule/blob/develop/src/test/java/de/skuzzle/enforcer/restrictimports/analyze/PackagePatternSpecifityTest.java#L34) file to have a glance at how _specificity_ works.In the above example, the first group is chosen by default (as by `basePackage=**`) unless a class is matched by the
more specific `basePackage` of the second group. In that case, only the definitions from the second group apply to this
class.## Static imports
> [!NOTE]
> Behavior of static import detection has been changed with version 2.0.0Every package pattern also automatically matches `static` imports. However, it is possible to explicitly mention the
`static` keyword in the pattern. In that case, the pattern will only match a resp. static import.Maven
```xml
static org.junit.Assert.*
```
Gradle (Kotlin)
```kotlin
restrictImports {
bannedImports = listOf("static org.junit.Assert.*")
}
```Gradle (Groovy)
```groovy
restrictImports {
bannedImports = ["static org.junit.Assert.*"]
}
```Inclusions and exclusion will work identically.
## Test code
By default, test code is also subject to the banned import checks (this is new since version `2.0.0`). You can disable
analysis of test code using the `includeTestCode` option.Maven
```xml
false
```
Gradle (Kotlin)
```kotlin
restrictImports {
includeTestCode = false
}
```Gradle (Groovy)
```groovy
restrictImports {
includeTestCode = false
}
```## Skipping
Using the configuration option `skip` you are able to temporarily disable a rule
instance.Maven
```xml
true
```
Gradle (Kotlin)
No direct equivalent
Gradle (Groovy)
No direct equivalent
If you want banned import analysis but without breaking your build you can set
`failBuild` to `false`.Maven
```xml
false
```
Gradle (Kotlin)
```kotlin
restrictImports {
failBuild = false
}
```Gradle (Groovy)
```groovy
restrictImports {
failBuild = false
}
```You can also pass these parameters as property to the maven build using `-Drestrictimports.skip` resp.
`-Drestrictimports.failBuild`. When passed as property, the property's value takes precedence over what has been
configured in the pom file.## Exclude source roots
By default, all source roots reported by Maven/Gradle are subject to the banned import checks, which for example includes but
is not limited to `${project.basedir}/src/main/java`, `${project.basedir}/src/test/java`,
`${project.build.directory}/generated-sources/main/java` and
`${project.build.directory}/generated-test-sources/main/java`. You can exclude source root(s) using the
`excludedSourceRoot(s)` option, either absolute or relative path.Maven
```xml
${project.build.directory}/generated-sources/main/java
target/generated-test-sources/main/java
```
Gradle (Kotlin)
No direct equivalent
Gradle (Groovy)
No direct equivalent
## Parallel Analysis
We support basic parallelization of the analysis. This is enabled by default but can be disabled either in the pom file
using the `` option or by passing `-Drestrictimports.parallel` to the maven build.Maven
```xml
false
```
Gradle (Kotlin)
```kotlin
restrictImports {
parallel = false
}
```Gradle (Groovy)
```groovy
restrictImports {
parallel = false
}
```## Detecting full qualified class usage
To overcome some of the limitations mentioned [here](#limitation), you can enable 'full compilation unit' parsing mode usingMaven
```xml
true
```
Gradle (Kotlin)
```kotlin
restrictImports {
parseFullCompilationUnit = true
}
```Gradle (Groovy)
```groovy
restrictImports {
parseFullCompilationUnit = true
}
```The option currently only affects parsing of java source files. When enabled, we will attempt a full parse of each
java source file, creating an actual AST. This allows to also detect full qualified class usages but will be
considerably slower.> [!WARNING]
> In case a source file cannot be properly parsed, we try to fall back to our _native_ line-by-line parsing
> approach described [here](#syntactical-limitation). A respective warning will be issued in the
> report that is generated at the end.
>
> This is especially the case when using Java language features introduced with version 16 or higher.
> See [#60](https://github.com/skuzzle/restrict-imports-enforcer-rule/issues/60) for details.## Package Patterns
Package patterns are dot separated strings that can be compared case sensitively part by part. Every part must adhere to
the java identifier rules except some special literals:1. `*` matches every package part but exactly one.
2. `**` matches multiple package parts but at least one.
3. `'*'` matches a literal `*` in an import statement.
4. `*SomeString` matches every package part that ends with `SomeString`.
5. `SomeString*` matches every package part that starts with `SomeString`.
6. `*SomeString*` matches every package part that contains `SomeString`.The pattern `java.util.*` matches `java.util.ArrayList` but not `java.util.regex.Pattern`.
Likewise, the pattern `java.util.**` matches all classes and subclasses contained in`java.util`.
Double wildcards are supported everywhere within a pattern. `**.*DumbName` would match every import which ends in `DumbName`.
Parts within a package must not be empty like in `foo..bar`.If a pattern does not contain any wildcards, matching degrades to a simple String
comparison.## Limitation
### Syntactical limitation
This rule implementation assumes that every analyzed java source file is syntactically
correct. If a source file is not, the analysis result is undefined. We don't use a formal parser to parse the whole
source file into an abstract syntax tree. Instead, import statements are extracted by relying on more or less simple
String split operations and only reading each source file up until a non-import statement (like class declaration) is
discovered. We cover a set of esoteric edge cases, for example block comments within a single import statement and the
like.> !NOTE]
> Plus side to this approach is, that we are mostly agnostic to the Java version you are using. Our parser doesn't
> need updates even if you want to use latest Java language features in your code base.### Conceptual limitation
Import recognition works by comparing the import statements within your source files
against the specified patterns. If your class uses wildcard imports like in```java
import java.util.*;
```this plugin will not be able to match that import against a banned pattern pointing to a
concrete class like `java.util.ArrayList`. However, wildcard recognition would still work
as expected.Also, it is not possible to detect full qualified class usages, where a type is used without an import statement.
For checking the `basePackage` and `exclusion` patterns, the plugin tries to construct the
_full qualified class name_ (FQCN) of each analyzed source file. It does so by
concatenating the file name to the source file's value of the `package ;`
statement. Thus if your `exclusion` pattern points to a concrete class like
`com.name.ClassName` the exclusion will only match if this class is declared in a file
with the exact name `ClassName.java`. The same applies in case you use a base package
pattern with no wild cards.## Full configuration example
Maven
```xml
true
false
false
true
${project.build.directory}/generated-sources/main/java
...
**
..
...
...
**
..
```
Gradle (Kotlin)
```kotlin
restrictImports {
reason = "..."
bannedImports = listOf("...")
allowedImports = listOf("...")
exclusions = listOf("...")
parallel = false
includeCompileCode = false
includeTestCode = false
parseFullCompilationUnit = false
}
```Gradle (Groovy)
```groovy
restrictImports {
reason = "..."
bannedImports = ["..."]
allowedImports = ["..."]
exclusions = ["..."]
parallel = false
includeCompileCode = false
includeTestCode = false
parseFullCompilationUnit = false
}
```