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 }