...
Code Block |
---|
|
public class BankOperations {
public BankOperations() {
if (!performSSNVerification()) {
throw new SecurityException("Invalid SSN!");
}
}
private boolean performSSNVerification() {
return false; // Returns true if data entered is valid, else false. Assume that the attacker just enters invalid SSN.
}
public void greet() {
System.out.println("Welcome user! You may now use all the features.");
}
}
public class UserApp {
public static void main(String[] args) {
BankOperations bo;
try {
bo = new BankOperations();
} catch(SecurityException ex) { bo = null; }
Storage.store(bo);
System.out.println("Proceed with normal logic");
}
}
public class Storage {
private static BankOperations bop;
public static void store(BankOperations bo) {
// Only store if it is not initialized
if (bop == null) {
if (bo == null) {
System.out.println("Invalid object!");
System.exit(1);
}
bop = bo;
}
}
}
|
...
Code Block |
---|
|
public final class BankOperations {
// ...
}
|
Noncompliant Code Example
The Javabeans pattern uses a no-argument constructor along with 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).
Compliant Solution
This compliant solution prevents a hostile caller from using a partially initialized instance of the class.
Code Block |
---|
|
public class BankOperations {
public BankOperations() |
Code Block |
---|
|
class UnsafeCurrency {
// total amount requested (required) this(performSSNVerification());
private}
int dollars
= -1; // initialize to default valueprivate BankOperations(boolean performSSNVerification) {
private int cents = -1; // initialize...
to default value}
//private changestatic requested,boolean denomination performSSNVerification(optional) {
private int quarters = 0;
private int dimes = 0;
private int nickels = 0;
private int pennies = 0;
public UnsafeCurrency() {} // no argument constructor
// setter methods
public void setDollar(int amount) { dollars = amount; } // Returns true if data entered is valid, else throws a SecurityException
// Assume that the attacker just enters invalid SSN; so this method always throws the exception
throw new SecurityException("Invalid SSN!");
}
public void setCentsgreet(int amount) {
cents = amount; }
public void setQuarters(int quantity) { quarters = quantity; }
public void setDimes(int quantity) { dimes = quantity; }
public void setNickels(int quantity) { nickels = quantity; }
public void setPennies(int quantity) { pennies = quantity; }
}
|
Compliant Solution
Wiki Markup |
---|
Use 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. 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. |
System.out.println("Welcome user! You may now use all the features.");
}
}
|
Compliant Solution
When the API can be extended (consider a non-final class, for example), it is permissible to use a flag to signal the successful completion of object construction. This is shown below.
Code Block |
---|
|
class BankOperations {
public volatile boolean initialized = false; // volatile flag
public BankOperations() {
if (!performSSNVerification()) {
throw new SecurityException("Invalid SSN!");
}
else {
initialized = true; // object construction succeeded
}
}
private boolean performSSNVerification() {
return false;
}
public void greet() {
if(initialized == true) {
System.out.println("Welcome user! You may now use all the features.");
// ...
}
else {
System.out.println("You are not permitted!");
}
}
}
|
Noncompliant Code Example
The Javabeans pattern uses a no-argument constructor along with 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).
Code Block |
---|
|
class UnsafeCurrency {
// total amount requested (required)
private int dollars = -1; // initialize to default value
private int cents = -1; // initialize to default value
// change requested, denomination (optional)
private int quarters = 0;
private int dimes = 0;
private int nickels = 0;
private int pennies |
Code Block |
---|
|
class Currency {
// 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 Currency(Builder builder) {
dollars = builder.dollars;
cents = builder.cents;
quarters = builder.quarters;
dimes = builder.dimes;
nickels = builder.nickels;
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;
public UnsafeCurrency() private{} int// nickelsno =argument 0;
constructor
// setter methods
public privatevoid setDollar(int penniesamount) { dollars = 0amount;
}
public void public BuildersetCents(int dollars, int centsamount) {
this.dollars cents = dollarsamount; }
public void setQuarters(int quantity) { this.centsquarters = centsquantity;
} }
public Buildervoid quarterssetDimes(int quantity) {
quarters dimes = quantity; }
public void setNickels(int quantity) return{ this;nickels
= quantity; }
public Buildervoid dimessetPennies(int quantity) {
pennies dimes = quantity; }
}
|
Compliant Solution
Wiki Markup |
---|
Use 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. 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. |
Code Block |
---|
|
class Currency {
// 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 Currency(Builder builder) {
dollars = builder.dollars;
cents = builder.cents;
quarters = builder.quarters;
dimes = builder.dimes;
nickels = builder.nickels;
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 Builder(int dollars, int cents) {
this.dollars = dollars;
this.cents = cents;
}
public Builder quarters(int quantity) {
quarters = quantity;
return this;
}
public Builder dimes(int quantity |
If input has 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 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.
Exceptions
EX1: When the API cannot be extended (consider a non-final class, for example), it is permissible to use a flag to signal the successful completion of object construction. This is shown below.
Code Block |
---|
|
class BankOperations {
public volatile boolean initialized = false; // volatile flag
public BankOperations() {
if (!performSSNVerification()) {
dimes = quantity;
throw new SecurityException("Invalid SSN!");return this;
}
public elseBuilder nickles(int quantity) {
initializednickels = truequantity;
// object constructionreturn succeededthis;
}
public }Builder pennies(int quantity) {
}
privatepennies boolean performSSNVerification() {
= quantity;
return falsethis;
}
public voidCurrency greetbuild() {
if(initialized == true)return {
new Currency(this);
System.out.println("Welcome user! You may now use all the features.");
// ...
}
else {
System.out.println("You are not permitted!");
}
}
}
}
}
}
Client code:
Currency USD = new Currency.Builder(100,56).quarters(2).nickles(1).pennies(1).build();
|
If input has 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 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.
Exceptions
EX1EX2: It is permissible to use the telescoping pattern when the overhead of the builder pattern is significant as compared to the number of parameters required to be initialized. This pattern prescribes a constructor to initialize the required parameters and individual constructors for each optional parameter that is added.
...