View Javadoc
1   /*
2    * junixsocket
3    *
4    * Copyright 2009-2024 Christian Kohlschütter
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.newsclub.net.unix;
19  
20  import java.io.Closeable;
21  import java.io.IOException;
22  import java.lang.ref.Cleaner;
23  
24  import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
25  
26  /**
27   * This wrapper (along with the Java 8-specific counterpart in src/main/java8) allows us to
28   * implement cleanup logic for objects that are garbage-collectable/no longer reachable.
29   *
30   * <p>
31   * Usage:
32   * <ol>
33   * <li>Create a subclass of CleanableState and attach it as a private field to the object you want
34   * to be cleaned up. You may call that field {@code cleanableState}.</li>
35   * <li>Define all resources that need to be cleaned up in this subclass (instead of the observed
36   * object itself).</li>
37   * <li>Make sure to not refer the observed object instance itself, as that will create a reference
38   * cycle and prevent proper cleanup.</li>
39   * <li>Implement the {@link #doClean()} method to perform all the necessary cleanup steps.</li>
40   * <li>If the observed class implements {@code close()}, it's a good practice to have it just call
41   * {@code cleanableState.runCleaner()}.</li>
42   * </ol>
43   * </p>
44   * <p>
45   * Implementation details:
46   * <ul>
47   * <li>In Java 9 or later, {@link Cleaner} is used under the hood.</li>
48   * <li>In Java 8 or earlier, {@link #finalize()} calls {@link #doClean()} directly.</li>
49   * </ul>
50   * </p>
51   *
52   * @author Christian Kohlschütter
53   */
54  @IgnoreJRERequirement // see src/main/java8
55  abstract class CleanableState implements Closeable {
56    private static final Cleaner CLEANER = Cleaner.create();
57    private final Cleaner.Cleanable cleanable;
58  
59    /**
60     * Creates a state object to be used as an implementation detail of the specified observed
61     * instance.
62     *
63     * @param observed The observed instance (the outer class referencing this
64     *          {@link CleanableState}).
65     */
66    protected CleanableState(Object observed) {
67      this.cleanable = CLEANER.register(observed, () -> doClean()); // NOPMD.LambdaCanBeMethodReference
68                                                                    // (Retrolambda)
69    }
70  
71    /**
72     * Explicitly the cleanup code defined in {@link #doClean()}. This is best be called from a
73     * {@code close()} method in the observed class.
74     */
75    public final void runCleaner() {
76      cleanable.clean();
77    }
78  
79    /**
80     * Performs the actual cleanup.
81     */
82    protected abstract void doClean();
83  
84    @Override
85    public final void close() throws IOException {
86      runCleaner();
87    }
88  }