/*
* 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 );
}
}
}