/* * Hibernate Search, full-text search for your domain model * * License: GNU Lesser General Public License (LGPL), version 2.1 or later * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. */ package org.hibernate.search.testsupport.leakdetection; import java.io.IOException; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.apache.lucene.store.DataInput; import org.apache.lucene.store.IOContext; import org.apache.lucene.store.IndexInput; import org.apache.lucene.store.IndexOutput; import org.apache.lucene.store.RAMDirectory; /** * This Directory keeps track of opened IndexInput and IndexOutput * instances, making it possible to verify if any file was left open. * * @author Sanne Grinovero (C) 2012 Red Hat Inc. */ public class FileMonitoringDirectory extends RAMDirectory { private final ConcurrentMap<IndexOutput,IndexOutput> openOutputs = new ConcurrentHashMap<IndexOutput, IndexOutput>( 10 ); private final ConcurrentMap<IndexInput,IndexInput> openInputs = new ConcurrentHashMap<IndexInput, IndexInput>( 40 ); @Override public IndexOutput createOutput(String name, IOContext context) throws IOException { IndexOutput indexOutput = super.createOutput( name, context ); IndexOutputDelegate tracked = new IndexOutputDelegate( indexOutput ); openOutputs.put( tracked, tracked ); return tracked; } @Override public IndexInput openInput(String name, IOContext context) throws IOException { IndexInput openInput = super.openInput( name, context ); IndexInputDelegate tracked = new IndexInputDelegate( openInput ); openInputs.put( tracked, tracked ); return tracked; } /** * @return true if this Directory was closed */ public boolean isClosed() { return isOpen == false; } /** * @return true if all files opened by this Directory were also closed */ public boolean allFilesWereClosed() { return openInputs.isEmpty() && openOutputs.isEmpty(); } private class IndexOutputDelegate extends IndexOutput { private final IndexOutput delegate; public IndexOutputDelegate(IndexOutput delegate) { super( "Testing Delegate: " + delegate.toString() ); this.delegate = delegate; } @Override public String toString() { return "IndexOutputDelegate to " + delegate.toString(); } @Override public void close() throws IOException { delegate.close(); openOutputs.remove( IndexOutputDelegate.this ); } // All remaining methods are generated as plain delegators, // except equals & hashcode : @Override public void writeByte(byte b) throws IOException { delegate.writeByte( b ); } @Override public void writeBytes(byte[] b, int length) throws IOException { delegate.writeBytes( b, length ); } @Override public long getFilePointer() { return delegate.getFilePointer(); } @Override public void writeBytes(byte[] b, int offset, int length) throws IOException { delegate.writeBytes( b, offset, length ); } @Override public void writeInt(int i) throws IOException { delegate.writeInt( i ); } @Override public void writeShort(short i) throws IOException { delegate.writeShort( i ); } @Override public void writeLong(long i) throws IOException { delegate.writeLong( i ); } @Override public void writeString(String s) throws IOException { delegate.writeString( s ); } @Override public void copyBytes(DataInput input, long numBytes) throws IOException { delegate.copyBytes( input, numBytes ); } @Override public void writeStringStringMap(Map<String, String> map) throws IOException { delegate.writeStringStringMap( map ); } @Override public void writeStringSet(Set<String> set) throws IOException { delegate.writeStringSet( set ); } @Override public long getChecksum() throws IOException { return delegate.getChecksum(); } } private class IndexInputDelegate extends IndexInput { private final IndexInput delegate; public IndexInputDelegate(IndexInput delegate) { super( delegate.toString() ); this.delegate = delegate; } @Override public void close() throws IOException { delegate.close(); openInputs.remove( IndexInputDelegate.this ); } @Override public String toString() { return "IndexInputDelegate to " + delegate.toString(); } // All remaining methods are generated as plain delegators, // except equals & hashcode : @Override public byte readByte() throws IOException { return delegate.readByte(); } @Override public void readBytes(byte[] b, int offset, int len) throws IOException { delegate.readBytes( b, offset, len ); } @Override public void readBytes(byte[] b, int offset, int len, boolean useBuffer) throws IOException { delegate.readBytes( b, offset, len, useBuffer ); } @Override public long getFilePointer() { return delegate.getFilePointer(); } @Override public void seek(long pos) throws IOException { delegate.seek( pos ); } @Override public long length() { return delegate.length(); } @Override public short readShort() throws IOException { return delegate.readShort(); } @Override public int readInt() throws IOException { return delegate.readInt(); } @Override public int readVInt() throws IOException { return delegate.readVInt(); } @Override public long readLong() throws IOException { return delegate.readLong(); } @Override public long readVLong() throws IOException { return delegate.readVLong(); } @Override public String readString() throws IOException { return delegate.readString(); } @Override public Map<String, String> readStringStringMap() throws IOException { return delegate.readStringStringMap(); } @Override public Set<String> readStringSet() throws IOException { return delegate.readStringSet(); } @Override public IndexInput slice(String sliceDescription, long offset, long length) throws IOException { return delegate.slice( sliceDescription, offset, length ); } @Override public void skipBytes(final long numBytes) throws IOException { delegate.skipBytes( numBytes ); } @Override public IndexInput clone() { //This is needed to make sure that the cloned instance can seek independently IndexInput clonedDelegate = (IndexInput) delegate.clone(); return new IndexInputDelegate( clonedDelegate ); } } }