Advanced Class Cycle Detection for Java

Hello Java community,

We’re excited to introduce advanced class cycle detection for Java projects with two new rules:

  • S7091 – Circular dependencies between classes across packages
  • S7027 – Circular dependencies between classes in the same package

These rules will highlight the most problematic class, while also displaying other involved classes as secondary locations. Multiple cycles in your classes are also reported, as shown in the screenshot below:

This is now available on all SonarCloud plans and will be included in SonarQube 10.8 (Developer Edition and above).

We’re currently working on introducing more advanced architecture and design features.

Please try it out and share any feedback!

6 Likes

Nice feature.
Yet for JPA/Hibernate bi-directional entities it’s practically unavoidable to have a cycle between classes (e.g. see https://www.baeldung.com/jpa-hibernate-associations#1-one-to-many-bidirectional-association)

5 Likes

I like the feature, but we also have false positive detections when using Jackson’s @JsonSubTypes.Type annotation (for an example, see Jackson Polymorphic Type Handling Annotations). Circular dependencies between base and child classes exist there “by design”.

4 Likes

Thanks for the feedback @lrozenblyum and @SlavikZ. We’ll look into these.

Feel free to share more of what works for you or doesn’t here!

Thanks for the feature, it is a good thing to consider. However, I also found other false positive detection (in addition to JPA entities) in the custom validation annotations creation: https://www.baeldung.com/spring-mvc-custom-validator

It marks the interface of the annotation and the usage in the ConstraintValidator as a cycle too. Unless there is a another way to create a custom validator, this also could be a false positive detection.

4 Likes

Thanks Orlando for sharing, and welcome to the sonar Community!
We are tracking all of these to find a good solution to reduce the noise.
Keep them coming!

1 Like

Considering:

public sealed class BaseRestController permits ChargingSessionRestController, SiteRestController, VehicleRestController, VehicleTypeRestController {}

and

public non-sealed class VehicleRestController extends BaseRestController {}

Sonar concludes that:
“This class is part of 4 cycles containing 5 classes”

I’d say this is a false positive. The permits clause doesn’t really generate a dependency, it generates a restriction. The only way to “fix” the cycle is to make BaseRestController not sealed.

3 Likes

The work I was doing got flagged by this new rule … it was somewhat of an effort to refactor to avoid cycles: in particular I ended up splitting some classes up, in the ugliest case into a superclass/subclass pair. The end result I think is cleaner and more maintainable.

In the ugly case, the class to be split up was conceptual very long, with many inner classes responsible for the implementation. I had already factored these inner classes out into separate files to reduce the file length. These caused multiple cycles (which would have been excluded by the inner class exclusion on the rule). My refactor, allowed the (design-wise inner) implementation classes to refer to the outer class by a base class, rather than by the subclass that

public class MyCoreClass implements ILargeInterface {
     int packageScopedInternals;
// ...
    public String query() {
        new Query(this).query();
    }
}

class Query {
   Query(MyCoreClass outer) {
      // ...
   }
   String query() {
      // ...
   }
}

to

abstract class MyCoreClassBase implements ILargeInterface {
     int packageScopedInternals;
// ...
}


public class MyCoreClass extends MyCoreClassBase {
    public String query() {
        new Query(this).query();
    }
}

class Query {
   Query(MyCoreClassBase outer) {
      // ...
   }
   String query() {
      // ...
   }
}

The proposal in the rule description to use an interface was unattractive, since I think this would have made some of the package scoped internals publicly accessible. Using a package scoped superclass avoid such spillage.

1 Like

Thanks for sharing Jeremy, it is an interesting example. Welcome to the Sonar Community!

Unfortunately we disabled these 2 rules due to the high number of false positives, mainly due to our usage of JSR 380 annotations and JPA/Hibernate (which make cycles unavoidable).
IMHO, S7027 is unrealistic.
Regarding the S7091 rule, I would suggest that the rule implementation ignores cycles appearing through the use of annotations (this would largely reduce false positives).

2 Likes

Hi Cédric,

Welcome to the Sonar Community and thank you for sharing.

Indeed, your feedback about JPA/Hibernate resonates with what we’ve been hearing from other users. I created a ticket to try to solve this in our next hardening.

It would be super helpful if you could share a minimal reproducer so we are sure we are fixing the problem you see on your end.

Absolutely agree

1 Like

Update:

The ticket related to annotations has been handled and will be shipping in the next release :tada:

If you previously disabled the rule, please give it another chance and let us know if it works better for you.

This came with a big revamp of the internals, so we can more easily handle this one and other nuanced cases. Kudos to the team!

See Hibernate One to Many Annotation Tutorial | Baeldung.
Entity (=class) A has a list/set/… of Enitity (=class) B. Entity B references back to entity A.

IMO it would make sense to make this rules configureable by defining a list of class-level annotations and base-classes to exclude.

e.g.

  1. exclude all classes annotated with jakarta.persistence.Entity-class-level-annotation.
  2. exclude all classes inheriting from com.mycompany.BaseDTO

side-note: Is there a way to have a look at the linked SONARCH-JIRA-tickets? They seem to be private.

1 Like

Hi, Gabriel Vivas

I saw that there is a planned improvement regarding S7027 related to JPA/Hibernate cycles. I have a few questions about this update:

  1. Which SonarQube version will include this fix?
  2. Which specific annotations will be skipped in the cycle detection?
  3. I don’t have access to the ticket mentioned in the discussion, so I’m unable to check the details. Could you provide more information on this?

Thanks in advance!

1 Like

Hi @leo_k , we fixed this problem in our SonarArchitecture plugin version 1.5., which is part of the SonarQube Server 2025.1 release from January 20th.

All annotations are now ignored for cycle detection, not just some specific ones.

Best,
Marco