Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Parasoft Jtest 2022.2

...

Both The Elements of Java Style [Vermeulen 2000] and the JPL Java Coding Standard [Havelund 2010] require that the dependency structure of a package

...

must never contain cycles

...

; that is, it must be representable as a directed acyclic graph (DAG).

There are several pros of eliminating Eliminating cycles between packages has several advantages:

  • Testing and maintainability: It is preferable to make a change somewhere (or patch) and limit the repercussions to as few packages as possible (ideally just one) as opposed to having to monitor or refine numerous packages.
  • Reusability: When a new version of a package is released, clients who reuse it do not have to test their existing code bases for compatibility with other packages that this particular package depends on. Sometimes, the reusable package evolves only to accommodate the changes to packages that it depends on.
  • . Cyclic dependencies magnify the repercussions of changes or patches to source code. Reducing the repercussions of changes simplifies testing and improves maintainability. Inability to perform adequate testing because of cyclic dependencies is a frequent source of security vulnerabilities.
  • Reusability. Cyclic dependencies between packages require that the packages be released and upgraded in lockstep. This requirement reduces reusability.
  • Releases and builds. Releases and builds: Avoiding cycles also helps to steer the development towards toward an environment that fosters modularization. Owners of different packages are also redeemed from relying on other bleeding-edge packages..
  • Deployment. Avoiding cyclic dependencies between packages reduces coupling between packages. Reduced coupling reduces the frequency of runtime errors such as ClassNotFoundError. This, in turn, simplifies deploymentDeployment: By resolving the cycles, deployment is simplified, as runtime errors like the infamous ClassNotFoundError, are reduced to a minimum by virtue of the toned down coupling between packages.

Noncompliant Code Example

This noncompliant code snippet features two different example contains packages named Account account and User user that consist of the classes AccountHolderClass and UserClass, AccountHolder, User, and UserDetails respectively. The class UserClass UserDetails extends from AccountHolderClass as an account holder can be any kind of user or owner. AccountHolderClass depends upon a few utility methods defined in UserClass and must declare its instance AccountHolder because a user is a kind of account holder. The class AccountHolder depends on a nonstatic utility method defined in the User class. Likewise, the UserClass UserDetails depends on AccountHolderClass but instead chooses to extend from it. This vicious use is one recipe for a circular dependency AccountHolder by extending it.

Code Block
bgColor#ffcccc

package Accountaccount;
import user.User.*;
public  class AccountHolderClassAccountHolder {

    private UserClassUser ucuser;
  //public usesvoid a class defined in package User
    protectedsetUser(User newUser) {user = newUser;}
 
  synchronized void depositFunds(String username, double amount) {
      // useUse a utility method of UserClassUser to check ifwhether username exists
    if  if(ucuser.exists(username)) { 
        // depositDeposit the amount
      } 
    }
  
  protected double getBalance(String accountNumber) { 
    /*/ returnReturn the account balance */ } 
    return 1.0; 
  }
}

package Useruser;
import Accountaccount.*AccountHolder;
public  class UserClassUserDetails extends AccountHolderClassAccountHolder {
    protectedpublic synchronized double getUserBalance(String accountNumber) {
      // useUse a method of AccountHolderClassAccountHolder to get the account balance 
      return getBalance(accountNumber); 
  }
}
 
public }
class User {
  public boolean exists(String username) { 
    /*/ checkCheck whether user exists* 
    return true; // }Exists
  }
}

Compliant Solution

The tight coupling between the classes in the two separate packages can be weakened by introducing an interface called BankApplication in a third package Bank, bank. The circular cyclic package dependency is eliminated by ensuring that the AccountHolderClass AccountHolder does not use an instance of UserClass, but depend on User but instead relies on the interface by importing the Bank package. bank package (and not by implementing the interface).

In this compliant solution, such functionality is achieved by introducing adding a parameter of the interface type BankApplication to the depositFunds() method. This solution gives the AccountHolderClass AccountHolder a solid contract to bank uponon. Additionally, UserClass UserDetails implements the interface and provides concrete implementations of the methods while at the same time , inheriting the others other methods from AccountHolderClass.

It might appear that the interface BankApplication contains superfluous methods such as depositFunds() and getBalance(). They are present so that if the subclass overrides these superclass methods, the latter retains the capability of internally invoking the subclass' methods polymorphically (like calling ba.getBalance() here, with a custom implementation of the method in UserClass).

AccountHolder.

Code Block
bgColor#ccccff

package Bankbank;
public interface BankApplication {   
  void depositFunds(BankApplication ba, String username, double amount);
  double getBalance(String accountNumber);
  double getUserBalance(String accountNumber);
  boolean exists(String username); 
}

package Accountaccount;
import Bankbank.*BankApplication;  // importImport from a third package
class AccountHolderClass  {	  
 AccountHolder  {
  private BankApplication ba;
  public void setBankApplication(BankApplication newBA) {
    ba = newBA;
  }

  public synchronized void depositFunds(BankApplication ba, 
      String username, double amount) {	
    // useUse a utility method of UserClass UserDetails 
    // to check ifwhether username exists
    if (ba.exists(username)) { 
      // depositDeposit the amount
    } 
  } 
  public double getBalance(String accountNumber) { 
    /*/ returnReturn the account balance 
  */  return 1.0; 
  }   
}

package Useruser;
import Accountaccount.*AccountHolder; // one One-way dependency
import Bankbank.*BankApplication;  // importImport from a third package
public class UserClassUserDetails extends AccountHolderClass AccountHolder 
      implements BankApplication {
  public synchronized double getUserBalance(
      String accountNumber) {
    // useUse a method of AccountHolderClassAccountHolder to get the account balance 
    return getBalance(accountNumber); 
  }
  public boolean exists(String username) { /*
 check if user exists*// returnCheck true; }
} 

package Implementer;
import Bank.*;
import Account.*;
import User.*;
class BankOperations {
  private BankApplication ba;
  public BankOperations(BankApplication ba) {
    this.ba = ba;
  }

  public void doUserActions() {
    System.out.println(ba.exists("user"));
    System.out.println(ba.getUserBalance("1111"));
  }

  public static void main(String[] args) {
    AccountHolderClass ac = new AccountHolderClass(); 
    ac.depositFunds(new UserClass(), "user", 1.0); // pass an interface argument
    BankOperations bo = new BankOperations(new UserClass()); 
    bo.doUserActions(); 
  }
} 

Risk Assessment

whether user exists
    return true; 
  }
}  

The interface BankApplication appears to contain superfluous methods such as depositFunds() and getBalance(). These methods are present so that if the subclass overrides them, the superclass retains the capability of internally invoking the subclass's methods polymorphically (for example, calling ba.getBalance() with an overridden implementation of the method in UserDetails). One consequence of this solution is that methods declared in the interface are required to be public in the classes that define them.

Applicability

Cyclic dependencies between packages can result in Cyclic dependencies between packages can lead to fragile builds. A security vulnerability in any a package will can easily percolate to several other packages.

...

Automated Detection

Severity Tool Likelihood Version Remediation Cost Checker Priority Description

Level

MSC06-J

low

probable

medium

P4

L3

References

Wiki Markup
\[[Martin 96|AA. Java References#Martin 96]\] 
\[[Knoernschild 01|AA. Java References#Knoernschild 01]\] Chapter 1: "OO Principles and Patterns, 1.2.5 Acyclic Dependencies Principle"

Parasoft Jtest
Include Page
Parasoft_V
Parasoft_V
CERT.DCL60.ACDEnsure that files do not contain cyclical dependencies

Bibliography

[Havelund 2010]JPL Coding Standard, Version 1.1

[Knoernschild 2002]

§1.2.5, "Acyclic Dependencies Principle"

[Martin 1996]

Chapter 1, "OO Principles and Patterns"

[Vermeulen 2000]The Elements of Java Style


...

Image Added Image Added Image AddedMSC38-J. Make sensitive classes noncloneable      49. Miscellaneous (MSC)      MSC30-J. Generate truly random numbers