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