Method chaining is a convenience mechanism that allows multiple method invocations on the same object to occur in a single statement. A method chaining implementation consists of a series of methods that return the {{this}} reference. This allows a caller to invoke methods in a chain by performing the next method invocation on the return value of the previous method in the chain.
While the methods used in method chaining may be atomic, a chain of method calls is inherently non-atomic. Consequently methods that are involved in method chaining should not be invoked by multiple threads unless the caller provides sufficient locking as illustrated in [CON07-J. Do not assume that a group of calls to independently atomic methods is atomic].
h2. Noncompliant Code Example
Method chaining is a useful design pattern for building an object and setting its optional fields. A class that supports method chaining provides several setter methods that each return the {{this}} reference. However, in a multithreaded environment, a thread may observe shared fields to contain inconsistent values. This noncompliant code example shows the JavaBeans pattern which is not safe for multithreaded use.
{code:bgColor=#FFcccc}
final class USCurrency {
// Change requested, denomination (optional fields)
private int quarters = 0;
private int dimes = 0;
private int nickels = 0;
private int pennies = 0;
public USCurrency() {}
// Setter methods
public USCurrency setQuarters(int quantity) {
quarters = quantity;
return this;
}
public USCurrency setDimes(int quantity) {
dimes = quantity;
return this;
}
public USCurrency setNickels(int quantity) {
nickels = quantity;
return this;
}
public USCurrency setPennies(int quantity) {
pennies = quantity;
return this;
}
}
// Client code:
private final USCurrency currency = new USCurrency();
// ...
new Thread(new Runnable() {
@Override public void run() {
currency.setQuarters(1).setDimes(1);
}
}).start();
new Thread(new Runnable() {
@Override public void run() {
currency.setQuarters(2).setDimes(2);
}
}).start();
{code}
The JavaBeans pattern uses a no-argument constructor and a series of parallel setter methods to build an object. This pattern is not thread-safe and can lead to inconsistent object state if the object is modified concurrently. In this noncompliant code example, the client constructs a {{USCurrency}} object and starts two threads that use method chaining to build the {{USCurrency}} object. This might result in inconsistent state, for example, {{currency}} might be left with two quarters and one dime or one quarter and two dimes.
h2. Compliant Solution
This compliant solution uses the variant of the Builder pattern \[[Gamma 95|AA. Java References#Gamma 95]\] suggested by Bloch \[[Bloch 08|AA. Java References#Bloch 08]\] to ensure thread safety and atomicity of object creation. {mc} What does this mean? It can be accessible to any number of threads ~DM => The method chaining is actually constrained to the {{USCurrency.Builder}} class which is only accessible from a single thread. {mc}
{code:bgColor=#ccccff}
final class USCurrency {
private final int quarters;
private final int dimes;
private final int nickels;
private final int pennies;
public USCurrency(Builder builder) {
this.quarters = builder.quarters;
this.dimes = builder.dimes;
this.nickels = builder.nickels;
this.pennies = builder.pennies;
}
// Static class member
public static class Builder {
private int quarters = 0;
private int dimes = 0;
private int nickels = 0;
private int pennies = 0;
public static Builder newInstance() {
return new Builder();
}
private Builder() {}
// Setter methods
public Builder setQuarters(int quantity) {
this.quarters = quantity;
return this;
}
public Builder setDimes(int quantity) {
this.dimes = quantity;
return this;
}
public Builder setNickels(int quantity) {
this.nickels = quantity;
return this;
}
public Builder setPennies(int quantity) {
this.pennies = quantity;
return this;
}
public USCurrency build() {
return new USCurrency(this);
}
}
}
// Client code:
private volatile USCurrency currency;
// ...
new Thread(new Runnable() {
@Override public void run() {
currency = USCurrency.Builder.newInstance().setQuarters(1).setDimes(1).build();
}
}).start();
new Thread(new Runnable() {
@Override public void run() {
currency = USCurrency.Builder.newInstance().setQuarters(2).setDimes(2).build();
}
}).start();
{code}
The {{Builder.newInstance()}} factory method is called with the _required_ arguments (if any) to obtain a {{Builder}} object. The _optional_ parameters are set using the setter methods of the builder. The object construction concludes with the invocation of the {{build()}} method. This pattern makes the class {{CurrencyUSCurrency}} immutable, and consequently, thread-safe.
Note that the {{currency}} field cannot be declared as final because it is set to a new immutable object from the threads. It is declared volatile in compliance with [CON09-J. Ensure visibility of shared references to immutable objects].
If input needs to be validated, ensure that the values are defensively copied prior to validation (see [FIO00-J. Defensively copy mutable inputs and mutable internal components] for more information). The builder class does not violate [SCP03-J. Do not expose sensitive private members of the outer class from within a nested class] because it maintains a copy of the variables defined in the scope of the containing class. These take precedence and as a result, do not break encapsulation.
h2. Risk Assessment
Using method chaining in multithreaded environments without performing external locking can lead to non-deterministic behavior.
|| Rule || Severity || Likelihood || Remediation Cost || Priority || Level ||
| CON30- J | low | probable | medium | {color:green}{*}P4{*}{color} | {color:green}{*}L3{*}{color} |
h3. Automated Detection
TODO
h3. Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the [CERT website|https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+FIO38-J].
h2. References
\[[API 06|AA. Java References#API 06]\]
\[[Bloch 08|AA. Java References#Bloch 08]\] Item 2: "Consider a builder when faced with many constructor parameters"
----
[!The CERT Sun Microsystems Secure Coding Standard for Java^button_arrow_left.png!|CON29-J. Do not execute interdependent tasks in a bounded thread pool] [!The CERT Sun Microsystems Secure Coding Standard for Java^button_arrow_up.png!|11. Concurrency (CON)] [!The CERT Sun Microsystems Secure Coding Standard for Java^button_arrow_right.png!|CON31-J. Avoid client-side locking when using classes that do not commit to their locking strategy]
|