Skip to content

ApplicationModules.detectViolations(…) fails when using ApplicationModuleSourceFactory #1554

@ddnyer

Description

@ddnyer

Spring Modulith version: 1.4.2 (the impacted code seems to be the same in the latest version)

Hi,

We use a custom ApplicationModuleSourceFactory to load generic modules defined in a shared library.

public class GenericApplicationModuleSourceFactory implements ApplicationModuleSourceFactory {

  @Override
  public List<String> getRootPackages() {
    return List.of("com.example.generic");
  }

  @Override
  public @Nullable ApplicationModuleDetectionStrategy getApplicationModuleDetectionStrategy() {
    return ApplicationModuleDetectionStrategy.explicitlyAnnotated();
  }
}

In another repo, an application is defined under the package com.example.app. It includes the shared library, including GenericApplicationModuleSourceFactory and the modules defined under com.example.generic.

The loading of the generic modules in our application works fine. But running the architecture tests with the following code fails with an unexpected exception.

import com.example.app.ModulithicApplication;

class ArchitectureTest {

  private final ApplicationModules modules =
      ApplicationModules.of(ModulithicApplication.class, location -> true);

  @Test
  void verifyArchitecture() {
    modules
        .detectViolations(VerificationOptions.defaults())
        .throwIfPresent();
  }
}

The exception raised has the following stacktrace where the user-management module is defined in the package com.example.generic.usermanagement.

Rule 'slices assigned from Appliction module slices [user-management] should be free of cycles' failed to check any classes. This means either that no classes have been passed to the rule at all, or that no classes passed to the rule matched the `that()` clause. To allow rules being evaluated without checking any classes you can either use `ArchRule.allowEmptyShould(true)` on a single rule or set the configuration property `archRule.failOnEmptyShould = false` to change the behavior globally.
java.lang.AssertionError: Rule 'slices assigned from Appliction module slices [user-management] should be free of cycles' failed to check any classes. This means either that no classes have been passed to the rule at all, or that no classes passed to the rule matched the `that()` clause. To allow rules being evaluated without checking any classes you can either use `ArchRule.allowEmptyShould(true)` on a single rule or set the configuration property `archRule.failOnEmptyShould = false` to change the behavior globally.
	at com.tngtech.archunit.lang.ArchRule$Factory$SimpleArchRule.verifyNoEmptyShouldIfEnabled(ArchRule.java:201)
	at com.tngtech.archunit.lang.ArchRule$Factory$SimpleArchRule.evaluate(ArchRule.java:181)
	at com.tngtech.archunit.lang.syntax.ObjectsShouldInternal.evaluate(ObjectsShouldInternal.java:76)
	at com.tngtech.archunit.library.dependencies.SliceRule.evaluate(SliceRule.java:99)
	at org.springframework.modulith.core.ApplicationModules.assertNoCyclesFor(ApplicationModules.java:627)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.AbstractList$RandomAccessSpliterator.forEachRemaining(AbstractList.java:722)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
	at org.springframework.modulith.core.ApplicationModules.detectViolations(ApplicationModules.java:496)
        ...

In ApplicationModules, the property allClasses includes classes under com.example.app, but not the classes of modules loaded with an ApplicationModuleSourceFactory (no class from com.example.generic). On the other hand, the field rootPackages does contain the root packages com.example.generic added by the factory. In ApplicationModules#detectViolations, the method ApplicationModules#assertNoCyclesFor is called for each rootPackages. This method fails on com.example.generic because allClasses does not contain the classes of that package, and the rule does not have SliceRule#allowEmptyShould(true).

Do you confirm that's a bug, or is there anything wrong with our configuration?

Let me know if you need a minimal example project to reproduce the issue.

Metadata

Metadata

Assignees

Labels

in: coreCore module meta modelmeta: waiting for feedbackWaiting for feedback of the original reportertype: bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions