Method chaining is a technique that defines several methods that return the this
reference of the current object. It is a convenience convenient mechanism that allows multiple method invocations on the same object to occur , in a single statement. However, unless special care is taken, the implementation may not be safe for multithreaded use. A method-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 VNA03-J. Do not assume that a group of calls to independently atomic methods is atomic.
Noncompliant Code Example
Several design patterns exist Method chaining is a useful design pattern for building an object and setting its optional fields. However, not all of them provide initialization safety. That isA class that supports method chaining provides several setter methods that each return the this
reference. However, if accessed concurrently, a thread may observe the object before its construction is overshared fields to contain inconsistent values. This noncompliant code example shows the unsafe Javabeans patternJavaBeans pattern, which is not thread-safe:
Code Block | ||
---|---|---|
| ||
final class CurrencyUSCurrency { // total amount requested (required) private int dollars = -1; // initialize to default value private int cents = -1; // initialize to default value // changeChange requested, denomination (optional fields) private int quarters = 0; private int dimes = 0; private int nickels = 0; private int pennies = 0; public CurrencyUSCurrency() {} // no argument constructor // setterSetter methods public CurrencyUSCurrency setDollarsetQuarters(int amountquantity) { dollarsquarters = amountquantity; return this; } public CurrencyUSCurrency setCentssetDimes(int amountquantity) { centsdimes = amountquantity; return this; } public CurrencyUSCurrency setQuarterssetNickels(int quantity) { quartersnickels = quantity; return this; } public CurrencyUSCurrency setDimessetPennies(int quantity) { dimespennies = quantity; return this; } } // Client code: class ExampleClientCode { public Currency setNickels(int quantity) { nickels = quantity; return this; } public Currency setPennies(int quantity) { pennies = quantityprivate final USCurrency currency = new USCurrency(); // ... public ExampleClientCode() { Thread t1 = new Thread(new Runnable() { @Override public void run() { currency.setQuarters(1).setDimes(1); return this } }); }t1.start(); // Client code: Currency currThread t2 = new Thread(new Currency(); curr.setDollar(10).setCents(50).setQuarters(42); Runnable() { @Override public void run() { currency.setQuarters(2).setDimes(2); } }); t2.start(); //... } } |
The JavaBeans The Javabeans pattern uses a no-argument constructor along with and a series of parallel setter methods to build an object. This pattern is not thread-safe and can lead to inconsistent object state . Moreover, it permits another thread to access the object even though it may only be partially initialized (not all required fields are initialized)when 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 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.
Compliant Solution
This compliant solution uses the variant of the Builder pattern 's \[ [Gamma 95|AA. Java References#Gamma 95]\] variant suggested by Bloch \[[Bloch 08|AA. Java References#Bloch 08]\] to ensure thread safety and atomicity of object creation. 1995], suggested by Bloch [Bloch 2008], to ensure the thread-safety and atomicity of object creation. Wiki Markup
Code Block | ||
---|---|---|
| ||
final class CurrencyUSCurrency { // total amount requested (required) private final int dollars; private final int cents; // change requested, denomination (optional) private final int quarters; private final int dimes; private final int nickels; private final int pennies; // Static class member private Currencypublic USCurrency(Builder builder) { dollars = builder.dollars; cents = builder.cents; this.quarters = builder.quarters; this.dimes = builder.dimes; this.nickels = builder.nickels; this.pennies = builder.pennies; } // Static class member public static class Builder { private final int dollars; private final int cents; private int quarters = 0; private int dimes = 0; private int nickels = 0; private int pennies = 0; public static Builder(int dollars, int cents newInstance() { this.dollarsreturn =new dollarsBuilder(); } this.cents = cents; private Builder() {} // }Setter methods public Builder quarterssetQuarters(int quantity) { this.quarters = quantity; return this; } public Builder dimessetDimes(int quantity) { this.dimes = quantity; return this; } public Builder nicklessetNickels(int quantity) { this.nickels = quantity; return this; } public Builder penniessetPennies(int quantity) { this.pennies = quantity; return this; } public CurrencyUSCurrency build() { return new CurrencyUSCurrency(this); } } } // Client code: Currency USD = new Currency.Builder(10,50).quarters(42).build(); 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 any required arguments to obtain a Builder
instance. The optional parameters are set using the setter methods of The idea is to call the constructor with the required parameters and obtain a builder object. Each optional parameter can be set using setters on the builder. The object construction concludes with the invocation of the build()
method. This also has the effect of making the class Currency
immutable. pattern makes the USCurrency
class immutable and consequently thread-safe.
Note that the currency
field cannot be declared final because it is assigned a new immutable object. It is, however, declared volatile in compliance with VNA01-J. Ensure visibility of shared references to immutable objects.
When input must be validated, ensure If input needs to be validated, make sure that the values are copied from the builder class to the containing class's fields prior to checking. The builder class does not violate SCP03defensively copied prior to validation (see OBJ06-J. Defensively copy mutable inputs and mutable internal components for more information). The 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 , maintain encapsulation.
If the number of fields is small, it is better to synchronize the setter methods instead.
Risk Assessment
Using thread-unsafe implementations of method chaining in multithreaded environments without performing external locking can lead to undefined nondeterministic behavior.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
CON30VNA04-J | low Low | unlikely Probable | high Medium | P1 P4 | L1 |
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 7, Avoid finalizers |
L3 |
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)