Index: lucene/src/java/org/apache/lucene/store/MMapDirectory.java =================================================================== --- lucene/src/java/org/apache/lucene/store/MMapDirectory.java (revision 998410) +++ lucene/src/java/org/apache/lucene/store/MMapDirectory.java (working copy) @@ -100,22 +100,106 @@ public MMapDirectory(File path) throws IOException { super(path, null); } + + private static enum Platform { + SUN { + @Override + final void cleanMapping(final ByteBuffer buffer) throws IOException { + try { + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Void run() throws Exception { + final Method getCleanerMethod = buffer.getClass().getMethod("cleaner"); + getCleanerMethod.setAccessible(true); + final Object cleaner = getCleanerMethod.invoke(buffer); + if (cleaner != null) { + cleaner.getClass().getMethod("clean") + .invoke(cleaner); + } + return null; + } + }); + } catch (PrivilegedActionException e) { + final IOException ioe = new IOException("unable to unmap the mapped buffer"); + ioe.initCause(e.getCause()); + throw ioe; + } + } + + @Override + final boolean isUnmapSupported() { + try { + Class.forName("sun.misc.Cleaner"); + Class.forName("java.nio.DirectByteBuffer").getMethod("cleaner"); + return true; + } catch (Exception e1) { + return false; + } + } + }, + + HARMONY { + @Override + final void cleanMapping(final ByteBuffer buffer) throws IOException { + try { + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Void run() throws Exception { + final Method freeMethod = buffer.getClass().getMethod("free"); + freeMethod.setAccessible(true); + freeMethod.invoke(buffer); + return null; + } + }); + } catch (PrivilegedActionException e) { + final IOException ioe = new IOException("unable to unmap the mapped buffer"); + ioe.initCause(e.getCause()); + throw ioe; + } + } + + @Override + final boolean isUnmapSupported() { + try { + Class.forName("java.nio.DirectByteBuffer").getMethod("free"); + return true; + } catch (Exception e1) { + return false; + } + } + }, + + // This one must be the last one, as it returns always "supported": + UNSUPPORTED { + @Override + final void cleanMapping(final ByteBuffer buffer) { + // do nothing + } + + @Override + final boolean isUnmapSupported() { + return true; + } + }; + + abstract void cleanMapping(final ByteBuffer buffer) throws IOException; + abstract boolean isUnmapSupported(); + }; /** * true, if this platform supports unmapping mmapped files. */ public static final boolean UNMAP_SUPPORTED; + static final Platform PLATFORM; static { - boolean v; - try { - Class.forName("sun.misc.Cleaner"); - Class.forName("java.nio.DirectByteBuffer") - .getMethod("cleaner"); - v = true; - } catch (Exception e) { - v = false; + Platform platform = null; + for (Platform p : Platform.values()) { + if (p.isUnmapSupported()) { + platform = p; + break; + } } - UNMAP_SUPPORTED = v; + assert platform != null; + PLATFORM = platform; + UNMAP_SUPPORTED = PLATFORM != Platform.UNSUPPORTED; } /** @@ -143,37 +227,8 @@ public boolean getUseUnmap() { return useUnmapHack; } - + /** - * Try to unmap the buffer, this method silently fails if no support - * for that in the JVM. On Windows, this leads to the fact, - * that mmapped files cannot be modified or deleted. - */ - final void cleanMapping(final ByteBuffer buffer) throws IOException { - if (useUnmapHack) { - try { - AccessController.doPrivileged(new PrivilegedExceptionAction() { - public Object run() throws Exception { - final Method getCleanerMethod = buffer.getClass() - .getMethod("cleaner"); - getCleanerMethod.setAccessible(true); - final Object cleaner = getCleanerMethod.invoke(buffer); - if (cleaner != null) { - cleaner.getClass().getMethod("clean") - .invoke(cleaner); - } - return null; - } - }); - } catch (PrivilegedActionException e) { - final IOException ioe = new IOException("unable to unmap the mapped buffer"); - ioe.initCause(e.getCause()); - throw ioe; - } - } - } - - /** * Sets the maximum chunk size (default is {@link Integer#MAX_VALUE} for * 64 bit JVMs and 256 MiBytes for 32 bit JVMs) used for memory mapping. * Especially on 32 bit platform, the address space can be very fragmented, @@ -259,6 +314,8 @@ @Override public Object clone() { + if (buffer == null) + throw new AlreadyClosedException("MMapIndexInput already closed"); MMapIndexInput clone = (MMapIndexInput)super.clone(); clone.isClone = true; clone.buffer = buffer.duplicate(); @@ -267,10 +324,10 @@ @Override public void close() throws IOException { - if (isClone || buffer == null) return; // unmap the buffer (if enabled) and at least unset it for GC try { - cleanMapping(buffer); + if (isClone || buffer == null) return; + if (useUnmapHack) PLATFORM.cleanMapping(buffer); } finally { buffer = null; } @@ -382,6 +439,8 @@ @Override public Object clone() { + if (buffers == null) + throw new AlreadyClosedException("MultiMMapIndexInput already closed"); MultiMMapIndexInput clone = (MultiMMapIndexInput)super.clone(); clone.isClone = true; clone.buffers = new ByteBuffer[buffers.length]; @@ -403,12 +462,12 @@ @Override public void close() throws IOException { - if (isClone || buffers == null) return; try { + if (isClone || buffers == null) return; for (int bufNr = 0; bufNr < buffers.length; bufNr++) { // unmap the buffer (if enabled) and at least unset it for GC try { - cleanMapping(buffers[bufNr]); + if (useUnmapHack) PLATFORM.cleanMapping(buffers[bufNr]); } finally { buffers[bufNr] = null; }