Refactored still more and simplified code where possible

This commit is contained in:
Lassi Immonen 2010-12-28 21:38:54 +00:00
parent 308d29d754
commit 948ff3835e
14 changed files with 450 additions and 307 deletions

View File

@ -1,7 +1,6 @@
package com.mysema.query.lucene.session;
import com.mysema.query.lucene.LuceneQuery;
import com.mysema.query.lucene.LuceneWriter;
public interface LuceneSession {

View File

@ -1,205 +0,0 @@
package com.mysema.query.lucene.session;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriter.MaxFieldLength;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.SimpleFSDirectory;
import org.apache.lucene.util.Version;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.mysema.query.QueryException;
public class LuceneSessionFactoryImpl implements LuceneSessionFactory {
private static final Logger logger = LoggerFactory.getLogger(LuceneSessionFactoryImpl.class);
private final Directory directory;
private final AtomicReference<IndexSearcher> searcher = new AtomicReference<IndexSearcher>();
public LuceneSessionFactoryImpl(String indexPath) throws IOException {
File folder = new File(indexPath);
if (!folder.exists() && !folder.mkdirs()) {
throw new IOException("Could not create directory: " + folder.getAbsolutePath());
}
try {
directory = new SimpleFSDirectory(folder);
} catch (IOException e) {
logger.error("Could not create lucene directory to " + folder.getAbsolutePath());
throw e;
}
}
public LuceneSessionFactoryImpl(Directory directory) {
this.directory = directory;
}
@Override
public LuceneSession getCurrentSession() {
return LuceneSessionHolder.getCurrentSession();
}
@Override
public LuceneSession openSession(boolean readOnly) {
return new LuceneSessionImpl(this, readOnly);
}
public LuceneWriterImpl getWriter(boolean createNew) {
try {
IndexWriter writer = null;
if (createNew == false) {
try {
writer =
new IndexWriter(directory, new StandardAnalyzer(Version.LUCENE_30), false,
MaxFieldLength.LIMITED);
} catch (FileNotFoundException e) {
// Convience to create a new index if it's not already
// existing
createNew = true;
}
}
if (createNew == true) {
writer =
new IndexWriter(directory, new StandardAnalyzer(Version.LUCENE_30), true,
MaxFieldLength.LIMITED);
}
return new LuceneWriterImpl(writer);
} catch (IOException e) {
throw new QueryException(e);
}
}
public void flush(LuceneSessionImpl session) {
LuceneWriterImpl writer = session.getWriter();
if (writer == null) {
return;
}
writer.commit();
// Close the reader, so the next query will get fresh reader
closeReader(session);
}
public IndexSearcher leaseSearcher() {
try {
if (searcher.get() == null) {
createNewSearcher(null);
}
// Checking do we need to refresh the reader
IndexSearcher is = searcher.get();
if (!is.getIndexReader().isCurrent()) {
// Underlying index has changed
// Decreasing the reference counter so that
// count can go to zero either here or
// when final searcher has done it's job
// This pairs with createNewSearcher incRef()
try {
is.getIndexReader().decRef();
} catch (IOException e) {
logger.error("Could not release index reader", e);
}
createNewSearcher(is);
}
// Incrementing reference as we lease this out
// This pairs with closeReaders decRef()
searcher.get().getIndexReader().incRef();
return searcher.get();
} catch (IOException e) {
throw new QueryException(e);
}
}
private IndexSearcher createNewSearcher(IndexSearcher expected) throws IOException {
IndexSearcher is = new IndexSearcher(directory);
if (!searcher.compareAndSet(expected, is)) {
// Some thread already created a new one so just close this
is.close();
} else {
// Incrementing the reference count first time
// We want to keep using the same reader until the index is changed
is.getIndexReader().incRef();
}
return searcher.get();
}
public void closeSession(LuceneSessionImpl session) {
QueryException readerException = null;
try {
closeReader(session);
} catch (QueryException e) {
readerException = e;
}
closeWriter(session);
if (readerException != null) {
throw readerException;
}
}
private void closeReader(LuceneSessionImpl session) {
if (session.getIndexSearcher() == null) {
return;
}
try {
// Decrementing the reader, if this is last reference,
// reader will be closed
session.getIndexSearcher().getIndexReader().decRef();
session.removeIndexSearcher();
} catch (IOException e) {
logger.error("Reader close failed", e);
throw new QueryException("Reader close failed", e);
}
}
private void closeWriter(LuceneSessionImpl session) {
// Always close writer
try {
LuceneWriterImpl writer = session.getWriter();
if (writer != null) {
// TODO What would be best way to control this?
writer.getIndexWriter().optimize();
writer.getIndexWriter().close();
}
} catch (IOException e) {
logger.error("Writer close failed", e);
try {
if (IndexWriter.isLocked(directory)) {
IndexWriter.unlock(directory);
}
} catch (IOException e1) {
logger.error("Lock release failed", e1);
throw new QueryException(e1);
}
throw new QueryException(e);
}
}
}

View File

@ -6,6 +6,9 @@ import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.mysema.query.lucene.session.impl.LuceneSessionFactoryImpl;
import com.mysema.query.lucene.session.impl.LuceneSessionHolder;
@Aspect
public class LuceneTransactionHandler {

View File

@ -1,4 +1,4 @@
package com.mysema.query.lucene;
package com.mysema.query.lucene.session;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.Term;

View File

@ -1,53 +0,0 @@
package com.mysema.query.lucene.session;
import java.io.IOException;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import com.mysema.query.QueryException;
import com.mysema.query.lucene.LuceneWriter;
public class LuceneWriterImpl implements LuceneWriter {
private IndexWriter writer;
public LuceneWriterImpl(IndexWriter writer) {
this.writer = writer;
}
@Override
public LuceneWriter addDocument(Document doc) {
try {
writer.addDocument(doc);
return this;
} catch (IOException e) {
throw new QueryException(e);
}
}
@Override
public LuceneWriter deleteDocuments(Term term) {
try {
writer.deleteDocuments(term);
return this;
} catch (IOException e) {
throw new QueryException(e);
}
}
public void commit() {
try {
writer.commit();
} catch (IOException e) {
throw new QueryException(e);
}
}
public IndexWriter getIndexWriter() {
return writer;
}
}

View File

@ -0,0 +1,21 @@
package com.mysema.query.lucene.session;
import com.mysema.query.QueryException;
public class NoSessionBoundException extends QueryException {
private static final long serialVersionUID = 4569418223905066659L;
public NoSessionBoundException(String msg) {
super(msg);
}
public NoSessionBoundException(String msg, Throwable t) {
super(msg, t);
}
public NoSessionBoundException(Throwable t) {
super(t);
}
}

View File

@ -0,0 +1,21 @@
package com.mysema.query.lucene.session;
import com.mysema.query.QueryException;
public class SessionClosedException extends QueryException {
private static final long serialVersionUID = 4569418223905066659L;
public SessionClosedException(String msg) {
super(msg);
}
public SessionClosedException(String msg, Throwable t) {
super(msg, t);
}
public SessionClosedException(Throwable t) {
super(t);
}
}

View File

@ -0,0 +1,21 @@
package com.mysema.query.lucene.session;
import com.mysema.query.QueryException;
public class SessionReadOnlyException extends QueryException {
private static final long serialVersionUID = -4561275733479044147L;
public SessionReadOnlyException(String msg) {
super(msg);
}
public SessionReadOnlyException(String msg, Throwable t) {
super(msg, t);
}
public SessionReadOnlyException(Throwable t) {
super(t);
}
}

View File

@ -0,0 +1,51 @@
package com.mysema.query.lucene.session.impl;
import java.io.IOException;
import org.apache.lucene.search.IndexSearcher;
import com.mysema.query.QueryException;
/**
* Simple wrapper to encapsulate searcher specific actions.
*
* @author laim
*
*/
public class LuceneSearcher {
private IndexSearcher searcher;
public LuceneSearcher(IndexSearcher searcher) {
this.searcher = searcher;
}
public void close() {
release();
}
public boolean isCurrent() {
try {
return searcher.getIndexReader().isCurrent();
} catch (IOException e) {
throw new QueryException(e);
}
}
public void release() {
try {
searcher.getIndexReader().decRef();
} catch (IOException e) {
throw new QueryException(e);
}
}
public void lease() {
searcher.getIndexReader().incRef();
}
public IndexSearcher getIndexSearcer() {
return searcher;
}
}

View File

@ -0,0 +1,114 @@
package com.mysema.query.lucene.session.impl;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.SimpleFSDirectory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.mysema.query.QueryException;
import com.mysema.query.lucene.session.LuceneSession;
import com.mysema.query.lucene.session.LuceneSessionFactory;
public class LuceneSessionFactoryImpl implements LuceneSessionFactory {
private static final Logger logger = LoggerFactory.getLogger(LuceneSessionFactoryImpl.class);
private final Directory directory;
private final AtomicReference<LuceneSearcher> searcher = new AtomicReference<LuceneSearcher>();
// private LuceneInternalsFactory factory = new LuceneInternalsFactory() {
// IndexSearcher
// }
//
// public static interface LuceneInternalsFactory {
//
// }
public LuceneSessionFactoryImpl(String indexPath) throws IOException {
File folder = new File(indexPath);
if (!folder.exists() && !folder.mkdirs()) {
throw new IOException("Could not create directory: " + folder.getAbsolutePath());
}
try {
directory = new SimpleFSDirectory(folder);
} catch (IOException e) {
logger.error("Could not create lucene directory to " + folder.getAbsolutePath());
throw e;
}
}
public LuceneSessionFactoryImpl(Directory directory) {
this.directory = directory;
}
@Override
public LuceneSession getCurrentSession() {
return LuceneSessionHolder.getCurrentSession();
}
@Override
public LuceneSession openSession(boolean readOnly) {
return new LuceneSessionImpl(this, readOnly);
}
public LuceneWriterImpl getWriter(boolean createNew) {
return new LuceneWriterImpl(directory, createNew);
}
public LuceneSearcher leaseSearcher() {
try {
if (searcher.get() == null) {
createNewSearcher(null);
}
// Checking do we need to refresh the reader
LuceneSearcher s = searcher.get();
if (!s.isCurrent()) {
// Underlying index has changed
// Decreasing the reference counter so that
// count can go to zero either here or
// when final searcher has done it's job
// This pairs with createNewSearcher incRef()
try {
s.release();
} catch (QueryException e) {
logger.error("Could not release index reader", e);
}
createNewSearcher(s);
}
// Incrementing reference as we lease this out
// This pairs with closeReaders decRef()
searcher.get().lease();
return searcher.get();
} catch (IOException e) {
throw new QueryException(e);
}
}
private LuceneSearcher createNewSearcher(LuceneSearcher expected) throws IOException {
LuceneSearcher s = new LuceneSearcher(new IndexSearcher(directory));
if (!searcher.compareAndSet(expected, s)) {
// Some thread already created a new one so just close this
s.close();
} else {
// Incrementing the reference count first time
// We want to keep using the same reader until the index is changed
s.lease();
}
return searcher.get();
}
}

View File

@ -1,6 +1,8 @@
package com.mysema.query.lucene.session;
package com.mysema.query.lucene.session.impl;
import com.mysema.query.lucene.session.LuceneSession;
import com.mysema.query.lucene.session.NoSessionBoundException;
import com.mysema.query.QueryException;
/**
* Holds the thread local session
@ -56,7 +58,7 @@ public final class LuceneSessionHolder {
private static LuceneSessionRef getSessionRef() {
if (!hasCurrentSession()) {
throw new QueryException("There is no session bound to local thread");
throw new NoSessionBoundException("There is no session bound to local thread");
}
return currentSessionRef.get();
}

View File

@ -1,28 +1,28 @@
package com.mysema.query.lucene.session;
import org.apache.lucene.search.IndexSearcher;
package com.mysema.query.lucene.session.impl;
import com.mysema.query.QueryException;
import com.mysema.query.lucene.LuceneQuery;
import com.mysema.query.lucene.LuceneSerializer;
import com.mysema.query.lucene.LuceneWriter;
import com.mysema.query.lucene.session.LuceneSession;
import com.mysema.query.lucene.session.LuceneWriter;
import com.mysema.query.lucene.session.SessionClosedException;
import com.mysema.query.lucene.session.SessionReadOnlyException;
public class LuceneSessionImpl implements LuceneSession {
private final boolean readOnly;
private boolean closed = false;
private final LuceneSessionFactoryImpl sessionFactory;
private IndexSearcher searcher;
private LuceneSearcher searcher;
private LuceneWriterImpl writer;
private final LuceneSerializer serializer = new LuceneSerializer(true, true);
public LuceneSessionImpl(LuceneSessionFactoryImpl sessionFactory,
boolean readOnly) {
public LuceneSessionImpl(LuceneSessionFactoryImpl sessionFactory, boolean readOnly) {
this.sessionFactory = sessionFactory;
this.readOnly = readOnly;
}
@ -30,16 +30,16 @@ public class LuceneSessionImpl implements LuceneSession {
@Override
public LuceneQuery createQuery() {
checkClosed();
return new LuceneQuery(serializer, getSearcher());
return new LuceneQuery(serializer, getSearcher().getIndexSearcer());
}
private IndexSearcher getSearcher() {
if(searcher == null) {
private LuceneSearcher getSearcher() {
if (searcher == null) {
searcher = sessionFactory.leaseSearcher();
}
return searcher;
}
@Override
public LuceneWriter beginAppend() {
checkClosed();
@ -54,48 +54,62 @@ public class LuceneSessionImpl implements LuceneSession {
private LuceneWriter createWriter(boolean createNew) {
if (readOnly) {
throw new QueryException("Read only session, cannot create writer");
throw new SessionReadOnlyException("Read only session, cannot create writer");
}
if (writer == null) {
writer = sessionFactory.getWriter(createNew);
}
return writer;
}
@Override
public void close() {
checkClosed();
sessionFactory.closeSession(this);
QueryException searcherException = null;
if (searcher != null) {
try {
searcher.close();
} catch (QueryException e) {
searcherException = e;
}
}
if (writer != null) {
writer.close();
}
if (searcherException != null) {
throw searcherException;
}
closed = true;
}
@Override
public void flush() {
checkClosed();
sessionFactory.flush(this);
}
public IndexSearcher getIndexSearcher() {
return searcher;
}
public void removeIndexSearcher() {
if (writer == null) {
return;
}
writer.commit();
if (searcher != null) {
searcher.close();
}
searcher = null;
}
public LuceneWriterImpl getWriter() {
return writer;
}
private void checkClosed() {
if (closed) {
throw new QueryException("Session is closed");
throw new SessionClosedException("Session is closed");
}
}
}

View File

@ -0,0 +1,102 @@
package com.mysema.query.lucene.session.impl;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriter.MaxFieldLength;
import org.apache.lucene.index.Term;
import org.apache.lucene.store.Directory;
import org.apache.lucene.util.Version;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.mysema.query.QueryException;
import com.mysema.query.lucene.session.LuceneWriter;
public class LuceneWriterImpl implements LuceneWriter {
private static final Logger logger = LoggerFactory.getLogger(LuceneWriterImpl.class);
private IndexWriter writer;
public LuceneWriterImpl(Directory directory, boolean createNew) {
try {
if (createNew == false) {
try {
writer =
new IndexWriter(directory, new StandardAnalyzer(Version.LUCENE_30), false,
MaxFieldLength.LIMITED);
} catch (FileNotFoundException e) {
// Convience to create a new index if it's not already
// existing
createNew = true;
}
}
if (createNew == true) {
writer =
new IndexWriter(directory, new StandardAnalyzer(Version.LUCENE_30), true,
MaxFieldLength.LIMITED);
}
} catch (IOException e) {
throw new QueryException(e);
}
}
@Override
public LuceneWriter addDocument(Document doc) {
try {
writer.addDocument(doc);
return this;
} catch (IOException e) {
throw new QueryException(e);
}
}
@Override
public LuceneWriter deleteDocuments(Term term) {
try {
writer.deleteDocuments(term);
return this;
} catch (IOException e) {
throw new QueryException(e);
}
}
public void commit() {
try {
writer.commit();
} catch (IOException e) {
throw new QueryException(e);
}
}
public void close() {
Directory directory = writer.getDirectory();
try {
// TODO What would be best way to control this?
writer.optimize();
writer.close();
} catch (IOException e) {
logger.error("Writer close failed", e);
try {
if (IndexWriter.isLocked(directory)) {
IndexWriter.unlock(directory);
}
} catch (IOException e1) {
logger.error("Lock release failed", e1);
throw new QueryException(e1);
}
throw new QueryException(e);
}
}
public IndexWriter getIndexWriter() {
return writer;
}
}

View File

@ -3,6 +3,7 @@ package com.mysema.query.lucene.session;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.nio.ReadOnlyBufferException;
import java.util.List;
import org.apache.lucene.document.Document;
@ -15,7 +16,9 @@ import org.apache.lucene.store.RAMDirectory;
import org.junit.Before;
import org.junit.Test;
import com.mysema.query.QueryException;
import com.mysema.query.lucene.LuceneQuery;
import com.mysema.query.lucene.session.impl.LuceneSessionFactoryImpl;
import com.mysema.query.types.path.NumberPath;
import com.mysema.query.types.path.StringPath;
@ -53,7 +56,6 @@ public class LuceneSessionFactoryTest {
assertEquals(1, results.size());
assertEquals("Jurassic Park", results.get(0).getField("title").stringValue());
//TODO This is still needed
query = session.createQuery();
long count = query.where(title.startsWith("Nummi")).count();
assertEquals(1, count);
@ -87,7 +89,64 @@ public class LuceneSessionFactoryTest {
LuceneQuery query1 = session.createQuery();
assertEquals(4, query1.where(year.gt(1800)).count());
//The old query still sees the same 3
assertEquals(3, query.count());
}
@Test(expected=NoSessionBoundException.class)
public void testCurrentSession() {
sessionFactory.getCurrentSession();
}
@Test(expected=SessionReadOnlyException.class)
public void testReadonly() {
LuceneSession session = sessionFactory.openSession(true);
session.beginOverwrite();
}
@Test(expected=SessionClosedException.class)
public void testSessionClosedCreate() {
LuceneSession session = sessionFactory.openSession(false);
session.close();
session.createQuery();
}
@Test(expected=SessionClosedException.class)
public void testSessionClosedAppend() {
LuceneSession session = sessionFactory.openSession(false);
session.close();
session.beginAppend();
}
@Test(expected=SessionClosedException.class)
public void testSessionClosedFlush() {
LuceneSession session = sessionFactory.openSession(false);
session.close();
session.flush();
}
@Test(expected=SessionClosedException.class)
public void testSessionClosedClosed() {
LuceneSession session = sessionFactory.openSession(false);
session.close();
session.close();
}
@Test(expected=SessionClosedException.class)
public void testSessionClosedOverwrite() {
LuceneSession session = sessionFactory.openSession(false);
session.close();
session.beginOverwrite();
}
@Test
public void testResourcesAreReleased() {
}
@ -97,14 +156,8 @@ public class LuceneSessionFactoryTest {
session.close();
}
@Test
public void testReadonly() {
}
private void createDocuments(LuceneSession session) {
session.beginAppend()