Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,6 @@ public class LibraryArchRules implements ArchRulesService {
}
```

You will also need to create a file at `src/archRules/resources/META-INF/services/com.netflix.nebula.archrules.core.ArchRulesService` and add the fully qualified name of your rules class to that file.

When authoring rules about the usage of your own library code, it is recommended to colocate your rules library in the
same project as the library code. The ArchRules plugin will publish the rules in a separate Jar, and the Runner plugin
will select that jar for running rules, but these rule classes will not end up in the runtime classpath.
Expand Down Expand Up @@ -143,7 +141,8 @@ archRules {

## How it works

The Archrules Library plugin produces a separate Jar for the `archRules` sourceset, which is exposed as an alternate variant of the library.
The Archrules Library plugin produces a separate Jar for the `archRules` sourceset, which is exposed as an alternate variant of the library. It also will automatically generate a `META-INF/services` file which contains a reference for each implementation of `com.netflix.nebula.archrules.core.ArchRulesService` to declare it as a service provider.
The Archrules Runner plugin uses a Java [ServiceLoader](https://devdocs.io/openjdk~25/java.base/java/util/serviceloader) to discover all implementations of `com.netflix.nebula.archrules.core.ArchRulesService` in the rule libraries.

## LICENSE

Expand Down
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
org.gradle.caching=true
org.gradle.configuration-cache=true
systemProp.nebula.features.coreLockingSupport=true
nebula.integTest=false
5 changes: 3 additions & 2 deletions nebula-archrules-core/gradle.lockfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.tngtech.archunit:archunit:1.4.1=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.netflix.nebula:archrules-deprecation:0.1.2=archRules
com.tngtech.archunit:archunit:1.4.1=archRules,compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
net.bytebuddy:byte-buddy:1.17.7=testCompileClasspath,testRuntimeClasspath
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath
org.assertj:assertj-core:3.27.6=testCompileClasspath,testRuntimeClasspath
Expand All @@ -14,5 +15,5 @@ org.junit.platform:junit-platform-engine:1.12.2=testRuntimeClasspath
org.junit.platform:junit-platform-launcher:1.12.2=testRuntimeClasspath
org.junit:junit-bom:5.12.2=testCompileClasspath,testRuntimeClasspath
org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
org.slf4j:slf4j-api:2.0.17=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.slf4j:slf4j-api:2.0.17=archRules,compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
empty=annotationProcessor,testAnnotationProcessor
8 changes: 6 additions & 2 deletions nebula-archrules-gradle-plugin/gradle.lockfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@
cglib:cglib-nodep:3.2.2=integTestRuntimeClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-annotations:2.20=compileClasspath,implementationDependenciesMetadata,integTestCompileClasspath,integTestImplementationDependenciesMetadata,integTestRuntimeClasspath,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
com.jayway.jsonpath:json-path:2.9.0=integTestCompileClasspath,integTestImplementationDependenciesMetadata,integTestRuntimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
com.netflix.nebula:archrules-deprecation:0.1.2=archRules
com.netflix.nebula:archrules-joda:0.1.2=archRules
com.netflix.nebula:archrules-testing-frameworks:0.1.2=archRules
com.netflix.nebula:nebula-archrules-core:0.1.5=archRules
com.netflix.nebula:nebula-test:11.8.0=integTestCompileClasspath,integTestImplementationDependenciesMetadata,integTestRuntimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
com.tngtech.archunit:archunit:1.4.1=compileClasspath,implementationDependenciesMetadata,integTestCompileClasspath,integTestImplementationDependenciesMetadata,integTestRuntimeClasspath,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
com.tngtech.archunit:archunit:1.4.1=archRules,compileClasspath,implementationDependenciesMetadata,integTestCompileClasspath,integTestImplementationDependenciesMetadata,integTestRuntimeClasspath,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
io.github.java-diff-utils:java-diff-utils:4.12=kotlinInternalAbiValidation
net.bytebuddy:byte-buddy:1.17.7=integTestCompileClasspath,integTestImplementationDependenciesMetadata,integTestRuntimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
net.javacrumbs.json-unit:json-unit-assertj:5.0.0=integTestCompileClasspath,integTestImplementationDependenciesMetadata,integTestRuntimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
Expand Down Expand Up @@ -52,7 +56,7 @@ org.junit:junit-bom:5.14.0=integTestCompileClasspath,integTestImplementationDepe
org.objenesis:objenesis:2.4=integTestRuntimeClasspath,testRuntimeClasspath
org.opentest4j:opentest4j:1.3.0=integTestCompileClasspath,integTestImplementationDependenciesMetadata,integTestRuntimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
org.ow2.asm:asm:9.3=integTestRuntimeClasspath,testRuntimeClasspath
org.slf4j:slf4j-api:2.0.17=compileClasspath,implementationDependenciesMetadata,integTestCompileClasspath,integTestImplementationDependenciesMetadata,integTestRuntimeClasspath,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
org.slf4j:slf4j-api:2.0.17=archRules,compileClasspath,implementationDependenciesMetadata,integTestCompileClasspath,integTestImplementationDependenciesMetadata,integTestRuntimeClasspath,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
tools.jackson.core:jackson-core:3.0.2=compileClasspath,implementationDependenciesMetadata,integTestCompileClasspath,integTestImplementationDependenciesMetadata,integTestRuntimeClasspath,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
tools.jackson.core:jackson-databind:3.0.2=compileClasspath,implementationDependenciesMetadata,integTestCompileClasspath,integTestImplementationDependenciesMetadata,integTestRuntimeClasspath,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
tools.jackson:jackson-bom:3.0.2=compileClasspath,implementationDependenciesMetadata,integTestCompileClasspath,integTestImplementationDependenciesMetadata,integTestRuntimeClasspath,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.netflix.nebula.archrules.gradle;

import com.tngtech.archunit.thirdparty.org.objectweb.asm.ClassVisitor;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static com.tngtech.archunit.thirdparty.org.objectweb.asm.Opcodes.ASM9;

class ArchRulesServiceVisitor extends ClassVisitor {
public List<String> getArchRuleServiceClasses() {
return archRuleServiceClasses;
}

private final List<String> archRuleServiceClasses = new ArrayList<>();

ArchRulesServiceVisitor() {
super(ASM9);
}

public void visit(int version, int access, String name,
String signature, String superName, String[] interfaces) {
if (Arrays.asList(interfaces).contains("com/netflix/nebula/archrules/core/ArchRulesService")) {
archRuleServiceClasses.add(name.replace("/", "."));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.netflix.nebula.archrules.gradle;

import com.tngtech.archunit.thirdparty.org.objectweb.asm.ClassReader;
import org.gradle.api.DefaultTask;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.*;
import org.jspecify.annotations.NonNull;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;

/**
* Generates a file in META-INF/services to allow Rule classes to be discovered by the Runner
*/
@CacheableTask
abstract public class GenerateServicesRegistryTask extends DefaultTask {
/**
* The classes declared in the archRules source set
*/
@InputFiles
@PathSensitive(PathSensitivity.RELATIVE)
abstract public ConfigurableFileCollection getRuleSourceClasses();

/**
* The file in META-INF/services to output to. It should be named com.netflix.nebula.archrules.core.ArchRulesService.
*/
@OutputFile
abstract public Property<@NonNull File> getArchRuleServicesFile();

@TaskAction
public void generate() throws IOException {
ArchRulesServiceVisitor visitor = new ArchRulesServiceVisitor();
getRuleSourceClasses().getAsFileTree().getFiles()
.stream()
.filter(it -> it.getName().endsWith(".class"))
.forEach(classFile -> {
try {
if(getLogger().isDebugEnabled()) {
getLogger().debug("Generating archive rules for {}", classFile.getName());
}
ClassReader cr = new ClassReader(Files.newInputStream(classFile.toPath(), StandardOpenOption.READ));
cr.accept(visitor, ClassReader.SKIP_DEBUG);
} catch (IOException e) {
getLogger().warn("Failed to read class file {}", classFile.getAbsolutePath(), e);
}
});

getArchRuleServicesFile().get().createNewFile();
String fileContent = String.join("\n", visitor.getArchRuleServiceClasses());
Files.writeString(getArchRuleServicesFile().get().toPath(), fileContent, StandardOpenOption.WRITE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,33 @@ class ArchrulesLibraryPlugin : Plugin<Project> {
override fun apply(project: Project) {
val version = determineVersion()
project.pluginManager.withPlugin("java") {
val ext = project.extensions.getByType<JavaPluginExtension>()
val archRulesSourceSet = ext.sourceSets.create("archRules")
val javaExt = project.extensions.getByType<JavaPluginExtension>()
val archRulesSourceSet = javaExt.sourceSets.create("archRules")
project.dependencies.add(
archRulesSourceSet.implementationConfigurationName,
"com.netflix.nebula:nebula-archrules-core:$version"
)
val generateServicesTask = project.tasks.register<GenerateServicesRegistryTask>("generateServicesRegistry"){
archRuleServicesFile.set(
project.layout.buildDirectory.file(
"resources/archRules/META-INF/services/com.netflix.nebula.archrules.core.ArchRulesService"
).map { it.asFile }
)
ruleSourceClasses.setFrom(archRulesSourceSet.output)
dependsOn(archRulesSourceSet.classesTaskName)
}
project.tasks.named(archRulesSourceSet.classesTaskName){
finalizedBy(generateServicesTask)
}
project.tasks.named("processArchRulesResources"){
finalizedBy(generateServicesTask)
}
val jarTask = project.tasks.register<Jar>("archRulesJar") {
description = "Assembles a jar archive containing the classes of the arch rules."
group = "build"
from(archRulesSourceSet.output)
archiveClassifier.set("arch-rules")
dependsOn(generateServicesTask)
}
registerRuntimeFeatureForSourceSet(project, archRulesSourceSet, jarTask)
project.pluginManager.withPlugin("jvm-test-suite") {
Expand All @@ -49,6 +65,16 @@ class ArchrulesLibraryPlugin : Plugin<Project> {
implementation(archRulesSourceSet.output)
implementation("com.netflix.nebula:nebula-archrules-core:$version")
}
javaExt.sourceSets.named("archRulesTest").configure {
project.tasks.named(compileJavaTaskName){
dependsOn(generateServicesTask)
}
project.pluginManager.withPlugin("org.jetbrains.kotlin.jvm") {
project.tasks.named(getCompileTaskName("kotlin")) {
dependsOn(generateServicesTask)
}
}
}
}
}
project.tasks.named("check") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,18 @@ class ArchrulesLibraryPluginTest {
.hasOutcome(TaskOutcome.SUCCESS, TaskOutcome.FROM_CACHE)
assertThat(result.task(":archRulesJar"))
.hasOutcome(TaskOutcome.SUCCESS)
assertThat(result.task(":generateServicesRegistry"))
.hasOutcome(TaskOutcome.SUCCESS, TaskOutcome.FROM_CACHE)
assertThat(result)
.hasNoMutableStateWarnings()
.hasNoDeprecationWarnings()

val serviceFile = projectDir.resolve("build/resources/archRules/META-INF/services/com.netflix.nebula.archrules.core.ArchRulesService")
assertThat(serviceFile)
.`as`("service file is created")
.exists()
.content().contains("com.example.library.LibraryArchRules")

assertThat(projectDir.resolve("build/libs/library-with-rules-0.0.1.jar"))
.`as`("Library Jar is created")
.exists()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,9 @@
package com.netflix.nebula.archrules.gradle

import nebula.test.dsl.*
import nebula.test.dsl.TestKitAssertions.assertThat
import nebula.test.dsl.main
import nebula.test.dsl.plugins
import nebula.test.dsl.properties
import nebula.test.dsl.repositories
import nebula.test.dsl.rootProject
import nebula.test.dsl.run
import nebula.test.dsl.settings
import nebula.test.dsl.sourceSet
import nebula.test.dsl.src
import nebula.test.dsl.test
import nebula.test.dsl.testProject
import net.javacrumbs.jsonunit.assertj.assertThatJson
import org.gradle.testfixtures.ProjectBuilder
import org.gradle.testkit.runner.TaskOutcome
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import org.junit.jupiter.params.ParameterizedTest
Expand Down Expand Up @@ -71,7 +59,7 @@ class ArchrulesRunnerPluginTest {
}
}

val result = runner.run("check", "--stacktrace", "-x", "test"){
val result = runner.run("check", "--stacktrace", "-x", "test") {
withGradleVersion(gradleVersion.version)
forwardOutput()
}
Expand Down Expand Up @@ -202,7 +190,8 @@ class ArchrulesRunnerPluginTest {
dependencies(
"""archRules("com.netflix.nebula:archrules-deprecation:0.1.+")"""
)
rawBuildScript("""
rawBuildScript(
"""
archRules {
consoleReportEnabled = false
}
Expand All @@ -220,7 +209,7 @@ archRules {
}
}

val result = runner.run("check", "--stacktrace", "-x", "test"){
val result = runner.run("check", "--stacktrace", "-x", "test") {
withGradleVersion(gradleVersion.version)
forwardOutput()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,6 @@ internal class IntegrationTest {
}
}

val serviceFile = projectDir
.resolve("library-with-rules/src/archRules/resources/META-INF/services/com.netflix.nebula.archrules.core.ArchRulesService")
serviceFile.parentFile.mkdirs()
serviceFile.writeText("com.example.library.LibraryArchRules")

val result = runner.run("check", "--stacktrace") {
withGradleVersion(gradleVersion.version)
forwardOutput()
Expand Down Expand Up @@ -135,11 +130,6 @@ internal class IntegrationTest {
}
}

val serviceFile = projectDir
.resolve("library-with-rules/src/archRules/resources/META-INF/services/com.netflix.nebula.archrules.core.ArchRulesService")
serviceFile.parentFile.mkdirs()
serviceFile.writeText("com.example.library.LibraryArchRules")

val result = runner.run("check", "--stacktrace") {
withGradleVersion(gradleVersion.version)
forwardOutput()
Expand Down Expand Up @@ -214,11 +204,6 @@ internal class IntegrationTest {
}
}

val serviceFile = projectDir
.resolve("library-with-rules/src/archRules/resources/META-INF/services/com.netflix.nebula.archrules.core.ArchRulesService")
serviceFile.parentFile.mkdirs()
serviceFile.writeText("com.example.library.LibraryArchRules")

val result = runner.run("check", "--stacktrace") {
withGradleVersion(gradleVersion.version)
forwardOutput()
Expand Down
6 changes: 3 additions & 3 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
pluginManagement {
plugins {
id("com.netflix.nebula.root") version ("24.+")
id("com.netflix.nebula.plugin-plugin") version ("24.+")
id("com.netflix.nebula.library") version ("24.+")
id("com.netflix.nebula.root") version ("25.0.2")
id("com.netflix.nebula.plugin-plugin") version ("25.0.2")
id("com.netflix.nebula.library") version ("25.0.2")
}
}
plugins {
Expand Down