Menu

#1364 Circular Dependancy falsely flagging Builder Pattern

3.0.1
closed-invalid
None
5
2015-02-21
2015-02-21
No

Findbugs is falsely reporting a circular dependency when a class implements the Builder pattern.

Here is a sample class that gets flagged as having a circular dependency between the User class and the Builder class.


import java.io.Serializable;
import java.util.UUID;

import jdk.nashorn.internal.ir.annotations.Immutable;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.cpicard.common.validation.EmailAddress;

/**

 * A user represents any user of the system. This object is for tracking which user is tied to a specific object and is not
 * intended to provide login validation.
 * 
 * @author Chris Picard, chris.s.picard@gmail.com
 * @version 1.0.0
 */
@Immutable
public final class User implements Serializable {
   private static final long serialVersionUID = 1L;
   private final UUID userId;
   private final String firstName;
   private final String lastName;
   private final String emailAddress;

   @SuppressWarnings("synthetic-access")
   private User(final Builder builder) {
      this.userId = builder.userId;
      this.firstName = builder.firstName;
      this.lastName = builder.lastName;
      this.emailAddress = builder.emailAddress;
   }

   public UUID getUserId() {
      return userId;
   }

   public String getFirstName() {
      return firstName;
   }

   public String getLastName() {
      return lastName;
   }

   public String getEmailAddress() {
      return emailAddress;
   }

   @Override
   public int hashCode() {
      final HashCodeBuilder builder = new HashCodeBuilder();
      builder.append(userId);
      builder.append(firstName);
      builder.append(lastName);
      builder.append(emailAddress);
      return builder.toHashCode();
   }

   @Override
   public boolean equals(final Object obj) {
      if (this == obj) {
         return true;
      }
      if (obj == null) {
         return false;
      }
      if (getClass() != obj.getClass()) {
         return false;
      }
      final User other = (User) obj;
      final EqualsBuilder builder = new EqualsBuilder();
      builder.append(this.userId, other.userId);
      builder.append(this.firstName, other.firstName);
      builder.append(this.lastName, other.lastName);
      builder.append(this.emailAddress, other.emailAddress);
      return builder.isEquals();
   }

   @Override
   public String toString() {
      return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
   }

   /**

    * Implementation of the builder pattern.
    * 
    * @author Chris Picard, chris.s.picard@gmail.com
    * @version 1.0.0
    */
   public static final class Builder {
      private UUID userId;
      private String firstName;
      private String lastName;
      private String emailAddress;

      public Builder setValues(final User user) {
         this.userId = user.getUserId();
         this.firstName = user.getFirstName();
         this.lastName = user.getLastName();
         this.emailAddress = user.getEmailAddress();
         return this;
      }

      public Builder setUserId(final UUID userId) {
         this.userId = userId;
         return this;
      }

      public Builder setFirstName(final String firstName) {
         this.firstName = firstName;
         return this;
      }

      public Builder setLastName(final String lastName) {
         this.lastName = lastName;
         return this;
      }

      public Builder setEmailAddress(@EmailAddress final String emailAddress) {
         this.emailAddress = emailAddress;
         return this;
      }

      @SuppressWarnings("synthetic-access")
      public User build() {
         if (userId == null) {
            userId = UUID.randomUUID();
         }
         if (StringUtils.isEmpty(firstName)) {
            throw new IllegalStateException("First name is not set");
         }
         return new User(this);
      }
   }
}

Discussion

  • Tagir Valeev

    Tagir Valeev - 2015-02-21

    I don't see any warnings issued by FindBugs in this code. Which version of FindBugs are you using? Please attach the resulting findbugs.xml report.

     
  • Chris Picard

    Chris Picard - 2015-02-21

    I am using the FindBugs Eclipse Plugin 3.0.0. Confidence level medium, min bug report 20, all categories and not hidden detectors enabled.

     
  • Tagir Valeev

    Tagir Valeev - 2015-02-21

    CD_CIRCULAR_DEPENDENCY is not a FindBugs bug pattern, it's issued by FB-contrib which is separate project not supported by our team. You should report this problem to FB-contrib developers, probably here or here.

     
  • Tagir Valeev

    Tagir Valeev - 2015-02-21
    • status: open --> closed-invalid
    • assigned_to: Tagir Valeev
     
  • Chris Picard

    Chris Picard - 2015-02-21

    Thanks for the clarification, I will keep in mind that they are a separate project int the future.

     

Log in to post a comment.

MongoDB Logo MongoDB