Method chaining is a convenience convenient mechanism that allows multiple method invocations on the same object to occur in a single statement. Classes that support A method chaining provide several setter -chaining implementation consists of a series of methods that return the this
reference. This implementation 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.
Although the methods used in method chaining can be atomic, the chain they comprise is inherently nonatomic. Consequently, callers of methods that are involved in method chaining must provide sufficient locking to guarantee that the entire chain of invocations is atomic, as shown in VNA03Method chaining should not be used in a multithreaded environment because chained invocations of a set of methods are non-atomic and, consequently, noncompliant with CON07-J. Do not assume that a group of calls to independently atomic methods is atomic.
Noncompliant Code Example
Method chaining is a useful design pattern for building an object and setting its optional fields. However, in a multithreaded environmentA class that supports method chaining provides several setter methods that each return the this
reference. However, if accessed concurrently, a thread may observe shared fields to contain inconsistent values. This noncompliant code example shows the Javabeans JavaBeans pattern, which is not safe for multithreaded use. thread-safe:
Code Block | ||
---|---|---|
| ||
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: class ExampleClientCode { private final USCurrency currency = new USCurrency(); // ... public ExampleClientCode() { Thread t1 = new Thread(new Runnable() { @Override public void run() { currency.setQuarters(1).setDimes(1); } }); t1.start(); Thread t2 = new Thread(new Runnable() { @Override public void run() { currency.setQuarters(2).setDimes(2); } }); t2.start(); //... } } |
The Javabeans 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 when the object is modified concurrently. In this noncompliant code example, a the client that constructs a USCurrency
object and starts two threads as shown may find the object to contain that use method chaining to set the optional values of the USCurrency
object. This example code might result in the USCurrency
instance being left in an inconsistent state, for example, with two quarters and one dime or one quarter and two dimes, contrary to what it expects.
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. Gamma 1995], suggested by Bloch [Bloch 2008], to ensure the thread-safety and atomicity of object creation. Wiki Markup
Code Block | ||
---|---|---|
| ||
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: class ExampleClientCode { private volatile USCurrency currency; // ... public ExampleClientCode() { Thread t1 = new Thread(new Runnable() { @Override public void run() { currency = USCurrency.Builder.newInstance(). setQuarters(1).setDimes(1).build(); } }); t1.start(); Thread t2 = new Thread(new Runnable() { @Override public void run() { currency = USCurrency.Builder.newInstance(). setQuarters(2).setDimes(2).build(); } }); t2.start(); //... } } |
The Builder.newInstance()
factory method is called with the any required arguments (if any) to obtain a Builder
object instance. The optional parameters are set using the setter methods of the builder. The object construction concludes with the invocation of the build()
method. This also pattern makes the USCurrency
class Currency
immutable , and consequently , thread-safe.
Note that the currency
field cannot be declared as final because it is set to assigned a new immutable object from the threads. It is, however, declared as volatile in compliance with CON09 VNA01-J. Ensure visibility of shared references to immutable objects.
If When input needs to must be validated, ensure that the values are defensively copied prior to the validation (see FIO00 OBJ06-J. Defensively copy mutable inputs and mutable internal components for more information). The builder class does not violate SCP03 Builder
class also complies with OBJ08-J. Do not expose sensitive private members of the an outer class from within a nested class because it maintains a copy of the variables defined in the scope of the containing class. These The private members within the nested class take precedence and, as a result, do not break encapsulation.
Exceptions
EX1: A class may use method chaining in a multithreaded environment if it sufficiently documents this fact. Client code must externally use some locking to ensure that the method calls are thread-safemaintain encapsulation.
Risk Assessment
Using method chaining in multithreaded environments without performing external locking can lead to non-deterministic nondeterministic behavior.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
CON30VNA04-J | low Low | probable Probable | medium Medium | P4 | L3 |
Automated Detection
TODO
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
Wiki Markup |
---|
\[[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" |
Bibliography
[API 2014] |
|
Item 2, "Consider a Builder When Faced with Many Constructor Parameters" |
...
FIO36-J. Do not create multiple buffered wrappers on an InputStream 09. Input Output (FIO) 09. Input Output (FIO)