1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.buildcache.hash;
20
21 import java.io.IOException;
22 import java.lang.reflect.Method;
23 import java.nio.ByteBuffer;
24 import java.nio.channels.FileChannel;
25 import java.nio.channels.FileChannel.MapMode;
26
27 import static org.apache.maven.buildcache.hash.ReflectionUtils.getField;
28 import static org.apache.maven.buildcache.hash.ReflectionUtils.getMethod;
29
30
31
32
33 public class CloseableBuffer implements AutoCloseable {
34
35 private static final Cleaner CLEANER = getJVMDependentCleaner();
36
37 private static Cleaner getJVMDependentCleaner() {
38 final String jsv = System.getProperty("java.specification.version", "9");
39 if (jsv.startsWith("1.")) {
40 return DirectCleaner.isSupported() ? new DirectCleaner() : new NoopCleaner();
41 } else {
42 return UnsafeCleaner.isSupported() ? new UnsafeCleaner() : new NoopCleaner();
43 }
44 }
45
46 public static CloseableBuffer directBuffer(int capacity) {
47 return new CloseableBuffer(ByteBuffer.allocateDirect(capacity));
48 }
49
50 public static CloseableBuffer mappedBuffer(FileChannel channel, MapMode mode) throws IOException {
51 return new CloseableBuffer(channel.map(mode, 0, channel.size()));
52 }
53
54 private ByteBuffer buffer;
55
56
57
58
59 private CloseableBuffer(ByteBuffer buffer) {
60
61 this.buffer = buffer;
62 }
63
64
65
66
67 public ByteBuffer getBuffer() {
68 return buffer;
69 }
70
71 @Override
72 public void close() {
73 boolean done = CLEANER.clean(buffer);
74 if (done) {
75 buffer = null;
76 }
77 }
78
79
80 private interface Cleaner {
81
82 boolean clean(ByteBuffer buffer);
83 }
84
85 private static class NoopCleaner implements Cleaner {
86
87 @Override
88 public boolean clean(ByteBuffer buffer) {
89 return false;
90 }
91 }
92
93 private static class DirectCleaner implements Cleaner {
94
95 private static final Method ATTACHMENT = getMethod("sun.nio.ch.DirectBuffer", "attachment");
96 private static final Method CLEANER = getMethod("sun.nio.ch.DirectBuffer", "cleaner");
97 private static final Method CLEAN = getMethod("sun.misc.Cleaner", "clean");
98
99 public static boolean isSupported() {
100 return ATTACHMENT != null && CLEAN != null && CLEANER != null;
101 }
102
103
104
105
106
107
108 @Override
109 public boolean clean(ByteBuffer buffer) {
110 try {
111 if (ATTACHMENT.invoke(buffer) == null) {
112 CLEAN.invoke(CLEANER.invoke(buffer));
113 return true;
114 }
115 } catch (Exception ignore) {
116 }
117 return false;
118 }
119 }
120
121 private static class UnsafeCleaner implements Cleaner {
122
123
124 private static final Method INVOKE_CLEANER = getMethod("sun.misc.Unsafe", "invokeCleaner", ByteBuffer.class);
125 private static final Object UNSAFE = getField("sun.misc.Unsafe", "theUnsafe");
126
127 public static boolean isSupported() {
128 return UNSAFE != null && INVOKE_CLEANER != null;
129 }
130
131
132
133
134
135
136 @Override
137 public boolean clean(ByteBuffer buffer) {
138 try {
139
140 INVOKE_CLEANER.invoke(UNSAFE, buffer);
141 return true;
142 } catch (Exception ignore) {
143 }
144 return false;
145 }
146 }
147 }