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 }