Background: I use the Java class InitialDirContext
to access LDAP directories. Unfortunately, it does not implement interface AutoCloseable
, so it cannot be used in try-with-resources blocks.
Here is the original code I wrote: (inspired by this answer)
final Properties props = new Properties();
// Populate 'props' here.
final InitialDirContext context = new InitialDirContext(props);
Exception e0 = null;
try {
// use 'context' here
}
catch (Exception e) {
// Only save a reference to the exception.
e0 = e;
// Why re-throw?
// If finally block does not throw, this exception must be thrown.
throw e;
}
finally {
try {
context.close();
}
catch (Exception e2) {
if (null != e0) {
e0.addSuppressed(e2);
// No need to re-throw 'e0' here. It was (re-)thrown above.
}
else {
throw e2;
}
}
}
Is this a safe, correct, and equivalent replacement?
try (final AutoCloseable dummy = () -> context.close()) {
// use 'context' here
}
I think the answer is yes, but I want to confirm. I tried Googling for this pattern, but I found nothing. It is so simple! Thus, I am suspicious it may not be correct.
Edit: I just found this answer with a similar pattern.
All objects managed by a try with resources statement must implement the AutoCloseable interface. Multiple AutoCloseable objects can be created within Java's try with resources block.
public interface AutoCloseable. An object that may hold resources (such as file or socket handles) until it is closed. The close() method of an AutoCloseable object is called automatically when exiting a try -with-resources block for which the object has been declared in the resource specification header.
The try -with-resources statement is a try statement that declares one or more resources. A resource is an object that must be closed after the program is finished with it. The try -with-resources statement ensures that each resource is closed at the end of the statement. Any object that implements java.
The try statement allows you to define a block of code to be tested for errors while it is being executed. The catch statement allows you to define a block of code to be executed, if an error occurs in the try block.
As explained in the other answer you linked to, it is not strictly equivalent because you have to either catch or throw Exception
from AutoCloseable.close()
and you must be sure not to do anything with context
after the try
block because it is not out of scope as if InitialDirContext
directly implemented AutoCloseable
. Still I agree with others that this workaround is quite nice.
Of course, you could also extend InitialDirContext
and make it implement AutoCloseable
directly or (for final classes) use a delegator pattern and wrap the target object.
package de.scrum_master.stackoverflow;
import javax.naming.NamingException;
import javax.naming.directory.InitialDirContext;
import java.util.Hashtable;
import java.util.Properties;
public class TryWithResourcesAutoCloseableWrapper {
public static void main(String[] args) throws NamingException {
final Properties props = new Properties();
props.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
variant1(props);
variant2(props);
variant3(props);
}
public static void variant1(Properties props) throws NamingException {
final InitialDirContext context = new InitialDirContext(props);
try (final AutoCloseable dummy = context::close) {
lookupMX(context);
}
catch (NamingException ne) {
throw ne;
}
catch (Exception e) {
e.printStackTrace();
}
}
public static void variant2(Properties props) throws NamingException {
final InitialDirContext context = new InitialDirContext(props);
try (final MyCloseable dummy = context::close) {
lookupMX(context);
}
}
public static void variant3(Properties props) throws NamingException {
try (final MyInitialDirContext context = new MyInitialDirContext(props)) {
lookupMX(context);
}
}
private static void lookupMX(InitialDirContext context) throws NamingException {
System.out.println(context.getAttributes("scrum-master.de", new String[] { "MX" }));
}
public interface MyCloseable extends AutoCloseable {
void close() throws NamingException;
}
public static class MyInitialDirContext extends InitialDirContext implements AutoCloseable {
public MyInitialDirContext(Hashtable<?, ?> environment) throws NamingException {
super(environment);
}
}
}
A few more thoughts about how to use these workarounds:
variant1
and variant2
come at the cost of dummy
objects which inside the try
block you will never use, unless you cast them to InitialDirContext
first. Instead, you could directly use the outer context
objects, of course, which is also what you suggested.variant3
the auto-closable context
object directly has the correct (sub-)type, so you can actually work with it seamlessly without casting or dummy object. This comes at the cost of a special subclass.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With