Skip to content

Commit

Permalink
HSEARCH-1090 Testcase for unclosed files
Browse files Browse the repository at this point in the history
  • Loading branch information
Sanne committed May 4, 2012
1 parent 77c5730 commit cea2e4c
Show file tree
Hide file tree
Showing 4 changed files with 481 additions and 1 deletion.
Expand Up @@ -40,7 +40,8 @@
*/
public class RAMDirectoryProvider implements DirectoryProvider<RAMDirectory> {

private final RAMDirectory directory = new RAMDirectory();
private final RAMDirectory directory = makeRAMDirectory();

private String indexName;
private Properties properties;

Expand Down Expand Up @@ -71,6 +72,14 @@ public void stop() {
directory.close();
}

/**
* To allow extensions to create different RAMDirectory flavours:
* @return the RAMDirectory this provider is going to manage
*/
protected RAMDirectory makeRAMDirectory() {
return new RAMDirectory();
}

@Override
public boolean equals(Object obj) {
// this code is actually broken since the value change after initialize call
Expand Down
@@ -0,0 +1,182 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* JBoss, Home of Professional Open Source
* Copyright 2012 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.hibernate.search.test.fileleaks;

import java.io.Serializable;
import java.util.Arrays;

import junit.framework.Assert;

import org.apache.lucene.search.MatchAllDocsQuery;
import org.hibernate.search.annotations.DocumentId;
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Indexed;
import org.hibernate.search.backend.spi.Work;
import org.hibernate.search.backend.spi.WorkType;
import org.hibernate.search.engine.spi.SearchFactoryImplementor;
import org.hibernate.search.indexes.impl.DirectoryBasedIndexManager;
import org.hibernate.search.query.engine.spi.HSQuery;
import org.hibernate.search.spi.SearchFactoryBuilder;
import org.hibernate.search.test.util.ManualConfiguration;
import org.hibernate.search.test.util.ManualTransactionContext;
import org.hibernate.search.test.util.leakdetection.FileMonitoringDirectory;
import org.hibernate.search.test.util.leakdetection.FileMonitoringDirectoryProvider;
import org.junit.Test;

/**
* Test for HSEARCH-1090: IndexReader leaks file handles on close
*
* @author Sanne Grinovero <sanne@hibernate.org> (C) 2012 Red Hat Inc.
*/
public class AllFilesClosedTest {

private SearchFactoryImplementor searchFactory;

@Test
public void testFileHandlesReleased() {
//We initialize the SearchFactory in the test itself as we want to test it's state *after* shutdown
searchFactory = initializeSearchFactory();
//extract the directories now, as they won't be available after SearchFactory#close :
FileMonitoringDirectory directoryOne = getDirectory( "index1" );
FileMonitoringDirectory directoryTwo = getDirectory( "index2" );
try {
doSomeOperations();
assertDirectoryOpen( directoryOne );
assertDirectoryOpen( directoryTwo );
assertAllFilesClosed( directoryTwo );
// directoryOne is using resource pooling
}
finally {
searchFactory.close();
}
assertAllFilesClosed( directoryOne );
assertAllFilesClosed( directoryTwo );
assertDirectoryClosed( directoryOne );
assertDirectoryClosed( directoryTwo );
}

/**
* Verifies all files in the Directory were closed
*/
private void assertAllFilesClosed(FileMonitoringDirectory directory) {
Assert.assertTrue( directory.allFilesWereClosed() );
}

/**
* Verifies the directory is closed
*/
private void assertDirectoryClosed(FileMonitoringDirectory directory) {
Assert.assertTrue( directory.isClosed() );
}

/**
* Verifies the directory is open
*/
private void assertDirectoryOpen(FileMonitoringDirectory directory) {
Assert.assertFalse( directory.isClosed() );
}

private FileMonitoringDirectory getDirectory(String indexName) {
DirectoryBasedIndexManager indexManager = (DirectoryBasedIndexManager) searchFactory.getAllIndexesManager().getIndexManager( indexName );
FileMonitoringDirectoryProvider directoryProvider = (FileMonitoringDirectoryProvider) indexManager.getDirectoryProvider();
FileMonitoringDirectory directory = (FileMonitoringDirectory) directoryProvider.getDirectory();
return directory;
}

/**
* The reported bugs were related to multithreaded operations, but
* we can actually trigger it with a sequence of read/writes: we need to re-open
* a dirty index to run some query on it.
*/
private void doSomeOperations() {
assertElementsInIndex( 0 );
storeDvd( 1, "Aliens" );
storeDvd( 2, "Predators" );
storeBook( 1, "Hibernate Search, second edition" );
assertElementsInIndex( 3 );
storeDvd( 2, "Prometheus" ); // This is an update
storeBook( 2, "Prometheus and the Eagle" );
assertElementsInIndex( 4 );
}

/**
* Run the actual query. Assert sizes to verify everything else is working.
* @param expected number of elements found in the index
*/
private void assertElementsInIndex(int expected) {
HSQuery hsQuery = searchFactory.createHSQuery();
hsQuery
.luceneQuery( new MatchAllDocsQuery() )
.targetedEntities( Arrays.asList( new Class<?>[]{ Book.class, Dvd.class } ) );
int resultSize = hsQuery.queryResultSize();
Assert.assertEquals( expected, resultSize );
}

private void storeBook(int id, String string) {
Book book = new Book();
book.id = id;
book.title = string;
storeObject( book, id );
}

private void storeDvd(int id, String dvdTitle) {
Dvd dvd1 = new Dvd();
dvd1.id = id;
dvd1.title = dvdTitle;
storeObject( dvd1, id );
}

private void storeObject(Object entity, Serializable id) {
Work work = new Work( entity, id, WorkType.UPDATE, false );
ManualTransactionContext tc = new ManualTransactionContext();
searchFactory.getWorker().performWork( work, tc );
tc.end();
}

private SearchFactoryImplementor initializeSearchFactory() {
ManualConfiguration cfg = new ManualConfiguration()
.addProperty( "hibernate.search.default.directory_provider", FileMonitoringDirectoryProvider.class.getName() )
.addProperty( "hibernate.search.default.reader.strategy", "shared" )
.addProperty( "hibernate.search.index2.reader.strategy", "not-shared" ) //close all readers closed aggressively
.addProperty( "hibernate.search.index2.exclusive_index_use", "false" ) //close all writers closed aggressively
.addClass( Book.class )
.addClass( Dvd.class )
;
return new SearchFactoryBuilder()
.configuration( cfg )
.buildSearchFactory();
}

/** Two mapped entities on two differently configured indexes **/

@Indexed(index="index1")
public static final class Dvd {
@DocumentId long id;
@Field String title;
}

@Indexed(index="index2")
public static final class Book {
@DocumentId long id;
@Field String title;
}

}

0 comments on commit cea2e4c

Please sign in to comment.