...
| Code Block |
|---|
|
// Interface ExceptionReporter
public interface ExceptionReporter {
public void setExceptionReporter(ExceptionReporter er);
public void report(Throwable exception);
}
// Class DefaultExceptionReporter
public class DefaultExceptionReporter implements ExceptionReporter {
public DefaultExceptionReporter(ExceptionReporter er) {
// Carry out initialization
// Incorrectly publishes the "this" reference
er.setExceptionReporter(this);
}
public final void setExceptionReporter(ExceptionReporter er) {
// Sets the reporter
}
public void report(Throwable exception) { /* default implementation */ }
}
|
The class MyExceptionReporter subclasses DefaultExceptionReporter with the intent of adding a logging mechanism that logs critical messages before an exception is reported.
| Code Block |
|---|
|
// Class MyExceptionReporter derives from DefaultExceptionReporter
public class MyExceptionReporter extends DefaultExceptionReporter {
private final Logger logger;
public MyExceptionReporter(ExceptionReporter er) {
super(er); // Calls superclass's constructor
logger = Logger.getLogger("com.organization.Log");
}
public void report(Throwable t) {
logger.log(Level.FINEST,"Loggable exception occurred",t);
}
}
|
The class MyExceptionReporter subclasses DefaultExceptionReporter with the intent of adding a logging mechanism that logs critical messages before an exception is reported. Its constructor invokes the superclass DefaultExceptionReporter's constructor (a mandatory first step) which publishes the exception reporter before the initialization of the subclass has concluded. Note that the subclass initialization consists of obtaining an instance of the default logger. Publishing the exception reporter is equivalent to setting it so that it can receive and handle exceptions from that point onwards.
If any exception occurs before the call to Logger.getLogger() in the subclass MyExceptionReporter, it is not logged. Instead, a NullPointerException is generated which may itself be consumed by the reporting mechanism.
This erroneous behavior results from the race condition between an oncoming exception and the initialization of the subclass MyExceptionReporter. If the exception comes too soon, it finds the subclass MyExceptionReporter in a compromised state. This behavior is especially counter intuitive because logger is declared final and is not expected to contain an unintialized value.
This issue can also occur when a an event listener is prematurely published. Consequently, it will start receiving event notifications even before the subclass's initialization has concluded.
...
This compliant solution defines a method setReporter() in class MyExceptionReporter. The method explicitly calls the superclass's setExceptionReporter() method, publishing a reference to its own Class object. It is not permissible to publish the reference in the constructor for MyExceptionReporter for reasons noted earlier in the noncompliant code example. Other code remains unchanged.
| Code Block |
|---|
|
public class MyExceptionReporter extends DefaultExceptionReporter {
// ...
public void setReporter(ExceptionReporter er) {
super.setExceptionReporter(this);
}
}
|
...
| Wiki Markup |
|---|
It is possible for the {{this}} reference to implicitly get leaked outside the scope \[[Goetz 02|AA. Java References#Goetz 02]\]. Consider inner classes that maintain a copy of the {{this}} reference of the outer object. In this noncompliant code example, the constructor for class {{BadExceptionReporterDefaultExceptionReporter}} uses an anonymous inner class to publish a {{filter()}} method. |
| Code Block |
|---|
|
public class BadExceptionReporterDefaultExceptionReporter implements ExceptionReporter {
public BadExceptionReporterDefaultExceptionReporter(ExceptionReporter er) {
er.setExceptionReporter(new DefaultExceptionReporter(er) {
public void report(Throwable t) {
filter(t);
}
});
}
public void filter(Throwable t) {
// Filters sensitive exceptions
}
public void setExceptionReporter(ExceptionReporter er) {
// Sets the reporter
}
public void report(Throwable exception) {
// Default implementation
}
}
|
...
| Code Block |
|---|
|
public class GoodExceptionReporterDefaultExceptionReporter implements ExceptionReporter {
private final DefaultExceptionReporter erdefaultER;
private GoodExceptionReporterDefaultExceptionReporter(ExceptionReporter excr) {
erdefaultER = new DefaultExceptionReporter(excr) {
public void report(Throwable t) {
filter(t);
}
};
}
public static GoodExceptionReporterDefaultExceptionReporter newInstance(ExceptionReporter excr) {
GoodExceptionReporterDefaultExceptionReporter gerder = new GoodExceptionReporterDefaultExceptionReporter(excr);
excr.setExceptionReporter(gerder.erdefaultER);
return gerder;
}
public void setExceptionReporter(ExceptionReporter er) { }
public void filter(Throwable t) { }
public void report(Throwable exception) { }
}
|
Because the constructor is private, untrusted code cannot create instances of the class, prohibiting the this reference from escaping. Using a public static factory method to create new instances also protects against publication of partially constructed objects (CON26-J. Do not publish partially-constructed objects).
Noncompliant Code Example (thread)
...
| Code Block |
|---|
|
public someConstructorSomeConstructor() {
thread = new MyThread(this);
thread.start();
}
|
...
| Code Block |
|---|
|
public someConstructorSomeConstructor() {
thread = new MyThread(this);
}
public void startThread() {
thread.start();
}
|
...