The correct idiom for a class that contains multiple lockable resources

For the user code, there are several options for properly closing multiple resources:

1. fitting c-resources

try (
  A a = new A();
  B b = new B();
  C c = new C()
) {
  // ...
}

Besides the good and short ones, this is also correct.

2. Guava Closer

For pre-JDK7, there is Guava Closer, which is used as:

Closer closer = Closer.create();
try {
  A a = closer.register(new A());
  B b = closer.register(new B());
  C c = closer.register(new C());
  // ...
} catch (Throwable e) { // must catch Throwable
  throw closer.rethrow(e);
} finally {
  closer.close();
}

, ( https://github.com/google/guava/wiki/ClosingResourcesExplained#closer )


, ?

, :

public class P implements AutoCloseable {
  private A a;
  private B b;
  private C c;

  public P() {
    a = new A();
    b = new B();
    c = new C();
  }

  public close() {
    c.close();
    b.close();
    a.close();
  }
}

:

  • , ( , close)
  • close,

1, 2. :

  • try-with-resources, , , P
  • Guava Closer . , ,

N ? 1 2

+4
3

execute. , , . . , .

public class ResourceWrapper {

    private A a;
    private B b;
    private C c;

    private ResourceWrapper() {
        // add try catch if you have to, after cleanup then throw exception if ithappens
        a = new A();
        b = new B();
        c = new C();
    }

    /**
     * add required operation methods
     */
    public ResourceWrapper op1() {
        // do some operations
        return this;
    }
    public ResourceWrapper op2() {
        // if additional add or different
        return this;
    }
    // close everything here
    private void close() {
        // check null if you have to
        // add try catch if you have to
        c.close();
        b.close();
        a.close();
    }

public static void use(Consumer<ResourceWrapper> consumer) {
    ResourceWrapper resource = null;
    try {
        resource = new ResourceWrapper();
        consumer.accept(resource);
    }
    finally {
        if(resource!=null) {
            resource.close();
        }
    }
}
}

public class SampleResourceUser {
    /*
     * This represents the user of the Resource,
     * User only cares about which operations that needs to be done on the resource.
     * Opening and closing the resource wrapped around the operation methods by the owner of the Resource.
     *
     */
    public static void main(String[] args) {
        ResourceWrapper.use(resource->resource.op1().op2());
    }
}
+1

, ( , close)

, , , .

,

, , .

:

, A, B and C,

  • try Exception
  • ,

1 (s) 2 Throwable getSuppressed.

, -, , , close () . , - .


try..catch. - , , , - . - close , ( getSuppressed ).

, - , , -.

Resources - , .

public class Resources implements AutoCloseable {
    private MyCloseable1 myCloseable1;
    private MyCloseable2 myCloseable2;

    public Resources() {
        try {
            myCloseable1 = new MyCloseable1();
            myCloseable2 = new MyCloseable2();
        } catch (Exception e) {
            close(false, myCloseable1, myCloseable2);
            throw new RuntimeException("Initialization failed");
        }
    }


    @Override
    public void close() throws Exception {
        close(true, myCloseable1, myCloseable2);
    }

    private void close(boolean throwExceptionIfFailed, AutoCloseable... autoCloseables)  {
        boolean closeFailed = false;
        for (AutoCloseable autoCloseable : autoCloseables) {
            try {
                if (autoCloseable != null) {
                    autoCloseable.close();
                }
            } catch (Exception e) {
                //Add logs here.
                closeFailed = true;
            }
        }
      /*
       Using Java 8 streams and reduce.
        closeFailed = Arrays.stream(autoCloseables)
                .filter(Objects::nonNull)
                .reduce(false, (isFailed, autoCloseable) -> {
                    try {
                        autoCloseable.close();
                    } catch (Exception e) {
                        return true;
                    }
                    return isFailed;
                }, (isFailed1, isFailed2) -> isFailed1 || isFailed2);
        */
        if (closeFailed && throwExceptionIfFailed) {
            throw new RuntimeException("Closing of Resources failed");
        }
    }
}

:

try (Resources resources = new Resources()) {
    ....

} catch (Exception e) {
    ....
}
+1

:

public close() throws ... {
    try (A aa = a;
         B bb = b;
         C cc = c) {
        // empty
    }
}

We simply use the standard try-with-resource mechanism to close previously opened resources. This will deal with cases where a, bor care nulland where calls close()cause an exception.

For the constructor:

public P() throws ... {
  try {
    a = new A();
    b = new B();
    c = new C();
  } finally {
     if (!(a != null && b != null && c != null)) {
         close();
     }
}

This is harder if you want to suppress the exceptions thrown by the constructor close().

+1
source

Source: https://habr.com/ru/post/1694731/


All Articles