1 package com.atlassian.bonnie;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.lang.reflect.InvocationTargetException;
6 import java.lang.reflect.Method;
7 import java.lang.reflect.Modifier;
8 import java.util.ArrayList;
9 import java.util.List;
10 import java.util.UUID;
11 import java.util.concurrent.Callable;
12 import java.util.concurrent.CountDownLatch;
13 import java.util.concurrent.ExecutorService;
14 import java.util.concurrent.Executors;
15 import java.util.concurrent.Future;
16 import static java.util.concurrent.TimeUnit.SECONDS;
17 import java.util.concurrent.atomic.AtomicBoolean;
18 import java.util.concurrent.atomic.AtomicReference;
19
20 import junit.framework.AssertionFailedError;
21 import junit.framework.TestCase;
22
23 import org.apache.lucene.analysis.standard.StandardAnalyzer;
24 import org.apache.lucene.document.Document;
25 import org.apache.lucene.document.Field;
26 import org.apache.lucene.index.IndexReader;
27 import org.apache.lucene.index.IndexWriter;
28 import org.apache.lucene.index.Term;
29 import org.apache.lucene.search.Hits;
30 import org.apache.lucene.search.IndexSearcher;
31 import org.apache.lucene.search.TermQuery;
32 import org.apache.lucene.store.RAMDirectory;
33
34 import com.atlassian.bonnie.search.SearcherInitialisation;
35
36 public class TestLuceneConnection extends TestCase
37 {
38 private static final String FIELD_NAME = "content.space";
39 private static final String FIELD_VALUE = "space";
40 private static final String FIELD_NAME_COUNT = "count";
41 private static final TermQuery QUERY = new TermQuery(new Term(FIELD_NAME, FIELD_VALUE));
42
43 private ILuceneConnection lcon;
44 private RAMDirectory directory = new RAMDirectory();
45
46 private static final ILuceneConnection.WriterAction NOOP_WRITER_ACTION = new ILuceneConnection.WriterAction() {
47 public void perform(IndexWriter writer) {}
48 };
49 private static final ILuceneConnection.SearcherAction NOOP_SEARCHER_ACTION = new ILuceneConnection.SearcherAction() {
50 public void perform(IndexSearcher searcher) {}
51 };
52
53 protected void setUp() throws Exception
54 {
55 super.setUp();
56 lcon = new LuceneConnection(directory, new StandardAnalyzer());
57 }
58
59 protected void tearDown() throws Exception
60 {
61 super.tearDown();
62 }
63
64 public void testIndexDirectoryIsUnlockedWhenConstructed() throws IOException
65 {
66 lcon.close();
67
68
69 IndexWriter writer = new IndexWriter(directory, new StandardAnalyzer(), false);
70 writer.addDocument(createDocument());
71
72 assertTrue("index directory should be locked by write above", IndexReader.isLocked(directory));
73
74 lcon = new LuceneConnection(directory, new StandardAnalyzer());
75 lcon.withWriter(NOOP_WRITER_ACTION);
76 lcon.close();
77
78 assertFalse("index directory should have been unlocked by opening and closing the connection", IndexReader.isLocked(directory));
79 }
80
81
82
83
84
85 public void testWithSearch() throws IOException
86 {
87 final int numDocs = 5;
88 addDocsToIndex(numDocs);
89
90 lcon.withSearch(new LuceneConnection.SearcherAction()
91 {
92 public void perform(IndexSearcher searcher) throws IOException
93 {
94 Hits hits = searcher.search(QUERY);
95 assertEquals(5, hits.length());
96 }
97 });
98 }
99
100
101
102
103
104
105
106 public void testDefaultConfiguration() throws Exception
107 {
108 lcon.withWriter(new LuceneConnection.WriterAction()
109 {
110 public void perform(IndexWriter writer)
111 {
112 assertEquals(4, writer.getMergeFactor());
113 assertEquals(300, writer.getMaxBufferedDocs());
114 assertEquals(5000, writer.getMaxMergeDocs());
115 assertTrue(writer.getUseCompoundFile());
116 }
117 });
118 lcon.withBatchUpdate(new LuceneConnection.BatchUpdateAction()
119 {
120 public void perform() throws Exception
121 {
122 lcon.withWriter(new LuceneConnection.WriterAction()
123 {
124 public void perform(IndexWriter writer)
125 {
126 assertEquals(50, writer.getMergeFactor());
127 assertEquals(300, writer.getMaxBufferedDocs());
128 assertEquals(Integer.MAX_VALUE, writer.getMaxMergeDocs());
129 assertTrue(writer.getUseCompoundFile());
130 }
131 });
132 }
133 });
134 }
135
136
137
138
139
140
141
142 public void testCustomConfiguration() throws Exception
143 {
144 LuceneConnection.Configuration config = new LuceneConnection.Configuration()
145 {
146
147 public int getBatchMaxBufferedDocs()
148 {
149 return 100;
150 }
151
152 public int getBatchMaxMergeDocs()
153 {
154 return 200;
155 }
156
157 public int getBatchMergeFactor()
158 {
159 return 300;
160 }
161
162 public int getInteractiveMaxBufferedDocs()
163 {
164 return 10;
165 }
166
167 public int getInteractiveMaxMergeDocs()
168 {
169 return 20;
170 }
171
172 public int getInteractiveMergeFactor()
173 {
174 return 30;
175 }
176
177 public int getMaxFieldLength()
178 {
179 return 1000000;
180 }
181
182 public boolean isCompoundIndexFileFormat()
183 {
184 return false;
185 }
186 };
187 assertLuceneConnectionConfig(new LuceneConnection(new RAMDirectory(), new StandardAnalyzer(), config));
188 assertLuceneConnectionConfig(new LuceneConnection(directory, new StandardAnalyzer(), config));
189 }
190
191 private void assertLuceneConnectionConfig(final LuceneConnection lcon)
192 {
193 lcon.withWriter(new LuceneConnection.WriterAction()
194 {
195 public void perform(IndexWriter writer)
196 {
197 assertEquals(30, writer.getMergeFactor());
198 assertEquals(10, writer.getMaxBufferedDocs());
199 assertEquals(20, writer.getMaxMergeDocs());
200 assertFalse(writer.getUseCompoundFile());
201 }
202 });
203
204 lcon.withBatchUpdate(new LuceneConnection.BatchUpdateAction()
205 {
206 public void perform() throws Exception
207 {
208 lcon.withWriter(new LuceneConnection.WriterAction()
209 {
210 public void perform(IndexWriter writer)
211 {
212 assertEquals(300, writer.getMergeFactor());
213 assertEquals(100, writer.getMaxBufferedDocs());
214 assertEquals(200, writer.getMaxMergeDocs());
215 assertFalse(writer.getUseCompoundFile());
216 }
217 });
218 }
219 });
220 }
221
222 public void testSearcherReuse()
223 {
224 final int numDocs = 5;
225 addDocsToIndex(numDocs);
226
227 final AtomicReference<IndexSearcher> searcherRef = new AtomicReference<IndexSearcher>(null);
228 lcon.withSearch(new LuceneConnection.SearcherAction()
229 {
230 public void perform(IndexSearcher searcher) throws IOException
231 {
232 searcherRef.set(searcher);
233 assertEquals(5, searcher.search(QUERY).length());
234 }
235 });
236
237 lcon.withSearch(new LuceneConnection.SearcherAction()
238 {
239 public void perform(IndexSearcher searcher) throws IOException
240 {
241 assertEquals(5, searcher.search(QUERY).length());
242
243 assertSame(searcherRef.get(), searcher);
244 searcherRef.set(searcher);
245 }
246 });
247
248 lcon.withWriter(new LuceneConnection.WriterAction()
249 {
250 public void perform(IndexWriter writer)
251 {
252 }
253 });
254
255 lcon.withSearch(new LuceneConnection.SearcherAction()
256 {
257 public void perform(IndexSearcher searcher) throws IOException
258 {
259 assertEquals(5, searcher.search(QUERY).length());
260
261 assertNotSame(searcherRef.get(), searcher);
262 searcherRef.set(searcher);
263 }
264 });
265 lcon.withSearch(new LuceneConnection.SearcherAction()
266 {
267 public void perform(IndexSearcher searcher) throws IOException
268 {
269 assertEquals(5, searcher.search(QUERY).length());
270
271 assertSame(searcherRef.get(), searcher);
272 searcherRef.set(searcher);
273 }
274 });
275 lcon.withReader(new ILuceneConnection.ReaderAction()
276 {
277 public Object perform(IndexReader reader)
278 {
279 return null;
280 }
281 });
282 lcon.withSearch(new LuceneConnection.SearcherAction()
283 {
284 public void perform(IndexSearcher searcher) throws IOException
285 {
286 assertEquals(5, searcher.search(QUERY).length());
287
288 assertSame(searcherRef.get(), searcher);
289 }
290 });
291 }
292
293 public void testWriterIsInteractiveModeConfiguration() throws Exception
294 {
295 lcon.withWriter(new LuceneConnection.WriterAction()
296 {
297 public void perform(IndexWriter writer)
298 {
299
300 assertEquals("Should be interactive mode maxMergeSettings", ILuceneConnection.DEFAULT_CONFIGURATION.getInteractiveMaxMergeDocs(), writer.getMaxMergeDocs());
301 assertEquals("Should be interactive mode maxBufferedSettings", ILuceneConnection.DEFAULT_CONFIGURATION.getInteractiveMaxBufferedDocs(), writer.getMaxBufferedDocs());
302 assertEquals("Should be interactive mode mergeFactorSettings", ILuceneConnection.DEFAULT_CONFIGURATION.getInteractiveMergeFactor(), writer.getMergeFactor());
303 assertEquals("Should be default maxField", ILuceneConnection.DEFAULT_CONFIGURATION.getMaxFieldLength(), writer.getMaxFieldLength());
304 }
305 });
306 }
307
308 public void testReaderReuse()
309 {
310 final int numDocs = 5;
311 addDocsToIndex(numDocs);
312
313 final AtomicReference<IndexReader> readerRef = new AtomicReference<IndexReader>(null);
314 lcon.withReader(new ILuceneConnection.ReaderAction()
315 {
316 public Object perform(IndexReader reader)
317 {
318 readerRef.set(reader);
319 assertEquals(5, reader.numDocs());
320 return null;
321 }
322 });
323
324 lcon.withReader(new ILuceneConnection.ReaderAction()
325 {
326 public Object perform(IndexReader reader)
327 {
328 assertEquals(5, reader.numDocs());
329
330 assertSame(readerRef.get(), reader);
331 readerRef.set(reader);
332 return null;
333 }
334 });
335
336 lcon.withWriter(new LuceneConnection.WriterAction()
337 {
338 public void perform(IndexWriter writer)
339 {
340 }
341 });
342
343 lcon.withReader(new ILuceneConnection.ReaderAction()
344 {
345 public Object perform(IndexReader reader)
346 {
347 assertEquals(5, reader.numDocs());
348
349 assertNotSame(readerRef.get(), reader);
350 readerRef.set(reader);
351 return null;
352 }
353 });
354 lcon.withReader(new ILuceneConnection.ReaderAction()
355 {
356 public Object perform(IndexReader reader)
357 {
358 assertEquals(5, reader.numDocs());
359
360 assertSame(readerRef.get(), reader);
361 readerRef.set(reader);
362 return null;
363 }
364 });
365 lcon.withReader(new ILuceneConnection.ReaderAction()
366 {
367 public Object perform(IndexReader reader)
368 {
369 assertEquals(5, reader.numDocs());
370
371 assertSame(readerRef.get(), reader);
372 readerRef.set(reader);
373 return null;
374 }
375 });
376 lcon.withSearch(new ILuceneConnection.SearcherAction()
377 {
378 public void perform(IndexSearcher searcher)
379 {
380 assertNotNull(searcher.getIndexReader());
381 assertSame(readerRef.get(), searcher.getIndexReader());
382 }
383 });
384 lcon.withReader(new ILuceneConnection.ReaderAction()
385 {
386 public Object perform(IndexReader reader)
387 {
388 assertEquals(5, reader.numDocs());
389
390 assertSame(readerRef.get(), reader);
391 return null;
392 }
393 });
394 }
395
396 public void testFileConstructor() throws IOException
397 {
398 final File tempDir = File.createTempFile(this.getClass().getName(), ".idx");
399 try
400 {
401 tempDir.delete();
402 lcon = new LuceneConnection(tempDir, new StandardAnalyzer(), ILuceneConnection.DEFAULT_CONFIGURATION);
403 assertTrue(IndexReader.indexExists(tempDir));
404 }
405 finally
406 {
407 tempDir.delete();
408 }
409 }
410
411 public void testOptimizeCallsCorrectly() throws IOException
412 {
413 lcon.close();
414
415 final AtomicBoolean optimizeCalled = new AtomicBoolean(false);
416 final IndexWriter mockWriter = new IndexWriter(directory, null, false)
417 {
418 public synchronized void optimize()
419 {
420 optimizeCalled.set(true);
421 }
422 };
423
424 lcon = new LuceneConnection(directory, new StandardAnalyzer(), ILuceneConnection.DEFAULT_CONFIGURATION)
425 {
426 public void withWriter(ILuceneConnection.WriterAction action) throws LuceneException
427 {
428 try
429 {
430 action.perform(mockWriter);
431 }
432 catch (IOException e)
433 {
434 throw new LuceneException(e);
435 }
436 }
437 };
438
439 lcon.optimize();
440
441 assertTrue(optimizeCalled.get());
442 }
443
444 public void testWriterBlocksDelete() throws InterruptedException
445 {
446 addDocsToIndex(5);
447
448 final ExecutorService pool = Executors.newSingleThreadExecutor();
449 final AtomicBoolean innerThreadStarted = new AtomicBoolean(false);
450 final CountDownLatch deleteComplete = new CountDownLatch(1);
451
452
453 lcon.withWriter(new ILuceneConnection.WriterAction()
454 {
455 public void perform(final IndexWriter writer) throws IOException
456 {
457 final CountDownLatch latch = new CountDownLatch(1);
458
459
460 Future<?> deleteTask = pool.submit(new Runnable()
461 {
462 public void run()
463 {
464 innerThreadStarted.set(true);
465
466
467 latch.countDown();
468
469
470 lcon.withWriter(new ILuceneConnection.WriterAction()
471 {
472 public void perform(IndexWriter writer) throws IOException
473 {
474 assertEquals("threaded withWriter: Added 2 documents not detected from thread", 7, writer.numDocs());
475 writer.deleteDocuments(new Term(FIELD_NAME_COUNT, "0"));
476 }
477 });
478 deleteComplete.countDown();
479 }
480 });
481
482
483 try
484 {
485 latch.await();
486 }
487 catch (InterruptedException e)
488 {
489 throw new RuntimeException(e);
490 }
491
492
493 assertTrue(innerThreadStarted.get());
494
495
496
497 assertFalse(deleteTask.isDone());
498
499 assertEquals(5, writer.docCount());
500
501 writer.addDocument(createDocument());
502 writer.addDocument(createDocument());
503
504 assertFalse(deleteTask.isDone());
505 }
506 });
507
508
509 deleteComplete.await();
510
511 lcon.withReader(new ILuceneConnection.ReaderAction()
512 {
513 public Object perform(IndexReader reader)
514 {
515
516 assertEquals(6, reader.numDocs());
517 return null;
518 }
519 });
520 assertEquals("threaded withReaderAndDeletes: Document not added in thread", 6, lcon.getNumDocs());
521 }
522
523
524 public void testDeleteBlocksWriter() throws InterruptedException
525 {
526 addDocsToIndex(5);
527
528 final ExecutorService pool = Executors.newSingleThreadExecutor();
529 final AtomicBoolean innerThreadStarted = new AtomicBoolean(false);
530 final CountDownLatch writeComplete = new CountDownLatch(1);
531 final CountDownLatch latch = new CountDownLatch(1);
532 final AtomicReference<AssertionFailedError> innerError = new AtomicReference<AssertionFailedError>(null);
533
534 lcon.withWriter(new ILuceneConnection.WriterAction()
535 {
536 public void perform(final IndexWriter writer) throws IOException
537 {
538
539 Future<?> writeTask = pool.submit(new Runnable()
540 {
541 public void run()
542 {
543 innerThreadStarted.set(true);
544
545 latch.countDown();
546
547 lcon.withWriter(new ILuceneConnection.WriterAction()
548 {
549 public void perform(IndexWriter writer) throws IOException
550 {
551
552 lcon.withReader(new ILuceneConnection.ReaderAction()
553 {
554
555 public Object perform(IndexReader r) throws IOException
556 {
557 try
558 {
559 assertEquals("threaded withReaderAndDeletes: deleted documents not detected from thread", 3, r.numDocs());
560 }
561 catch (AssertionFailedError e)
562 {
563 innerError.set(e);
564 }
565 return null;
566 }
567 });
568 writer.addDocument(createDocument());
569 }
570 });
571 writeComplete.countDown();
572 }
573 });
574
575
576 try
577 {
578 latch.await();
579 }
580 catch (InterruptedException e)
581 {
582 throw new RuntimeException(e);
583 }
584
585
586 assertTrue(innerThreadStarted.get());
587
588
589 assertFalse(writeTask.isDone());
590
591
592 writer.deleteDocuments(new Term(FIELD_NAME_COUNT, "0"));
593 writer.deleteDocuments(new Term(FIELD_NAME_COUNT, "1"));
594 }
595 });
596
597
598 writeComplete.await();
599
600 lcon.withReader(new ILuceneConnection.ReaderAction()
601 {
602 public Object perform(IndexReader reader)
603 {
604
605 assertEquals(4, reader.numDocs());
606 return null;
607 }
608 });
609 assertEquals("threaded withReaderAndDeletes: Document not added in thread", 4, lcon.getNumDocs());
610 if (innerError.get() != null)
611 throw innerError.get();
612 }
613
614 public void testBatchUpdateHoldsLock() throws Exception
615 {
616 final CountDownLatch latch = new CountDownLatch(1);
617 final ExecutorService pool = Executors.newSingleThreadExecutor();
618 final AtomicReference<Future<?>> secondUpdateFuture = new AtomicReference<Future<?>>(null);
619
620 lcon.withBatchUpdate(new ILuceneConnection.BatchUpdateAction()
621 {
622 public void perform() throws Exception
623 {
624 secondUpdateFuture.set(
625 pool.submit(
626 new Runnable()
627 {
628 public void run()
629 {
630 latch.countDown();
631 lcon.withWriter(
632 new ILuceneConnection.WriterAction()
633 {
634 public void perform(IndexWriter writer)
635 {
636 assertNotNull(writer);
637 }
638 }
639 );
640 }
641 }
642 )
643 );
644 latch.await();
645 Thread.yield();
646 lcon.withWriter(
647 new ILuceneConnection.WriterAction()
648 {
649 public void perform(IndexWriter writer)
650 {
651 assertNotNull(writer);
652 assertEquals("Should be batch mode maxMergeSettings", ILuceneConnection.DEFAULT_CONFIGURATION.getBatchMaxMergeDocs(), writer.getMaxMergeDocs());
653 assertEquals("Should be batch mode maxBufferedSettings", ILuceneConnection.DEFAULT_CONFIGURATION.getBatchMaxBufferedDocs(), writer.getMaxBufferedDocs());
654 assertEquals("Should be batch mode mergeFactorSettings", ILuceneConnection.DEFAULT_CONFIGURATION.getBatchMergeFactor(), writer.getMergeFactor());
655 assertEquals("Should be default maxField", ILuceneConnection.DEFAULT_CONFIGURATION.getMaxFieldLength(), writer.getMaxFieldLength());
656 }
657 }
658 );
659
660 Future<?> future = secondUpdateFuture.get();
661 assertFalse(future.isDone());
662 assertFalse(future.isCancelled());
663 }
664 });
665 Future<?> future = secondUpdateFuture.get();
666 assertNull(future.get());
667 assertTrue(future.isDone());
668 assertFalse(future.isCancelled());
669 }
670
671 public void testWriterDoesNotBlockSearches()
672 {
673 addDocsToIndex(5);
674
675 final int noOfSearches = 1000;
676 final ExecutorService pool = Executors.newFixedThreadPool(4);
677 final CountDownLatch complete = new CountDownLatch(noOfSearches);
678 final AtomicReference<IndexReader> readerRef = new AtomicReference<IndexReader>(null);
679
680 lcon.withReader(
681 new ILuceneConnection.ReaderAction()
682 {
683 public Object perform(IndexReader reader)
684 {
685 readerRef.set(reader);
686 return null;
687 }
688 }
689 );
690
691
692 lcon.withWriter(new ILuceneConnection.WriterAction()
693 {
694 public void perform(final IndexWriter writer)
695 {
696 for (int i = 0; i < noOfSearches; i++)
697 {
698
699
700 pool.submit(
701 new Runnable()
702 {
703 public void run()
704 {
705
706 lcon.withSearch(new ILuceneConnection.SearcherAction()
707 {
708 public void perform(IndexSearcher searcher)
709 {
710 assertSame(readerRef.get(), searcher.getIndexReader());
711 }
712 });
713 lcon.withReader(new ILuceneConnection.ReaderAction()
714 {
715 public Object perform(IndexReader reader)
716 {
717 assertSame(readerRef.get(), reader);
718 return null;
719 }
720 });
721
722 complete.countDown();
723 }
724 }
725 );
726 }
727
728
729
730 try
731 {
732 assertTrue("We waited too long, should have passed", complete.await(5, SECONDS));
733 }
734 catch (InterruptedException e)
735 {
736 throw new AssertionError("We should not have to wait here, tasks should be reentrant: " + e);
737 }
738 }
739 });
740
741
742 lcon.withReader(
743 new ILuceneConnection.ReaderAction()
744 {
745 public Object perform(IndexReader reader)
746 {
747 assertNotSame(readerRef.get(), reader);
748 return null;
749 }
750 }
751 );
752 }
753
754 public void testBatchUpdatesAreNotVisibleToSearchers() throws Exception
755 {
756 addDocsToIndex(5);
757
758 final Callable<Integer> countDocs = new Callable<Integer>() {
759 public Integer call() throws Exception
760 {
761 return (Integer) lcon.withReader(new ILuceneConnection.ReaderAction() {
762 public Object perform(IndexReader reader)
763 {
764 return reader.numDocs();
765 }
766 });
767 }
768 };
769
770 lcon.withBatchUpdate(new ILuceneConnection.BatchUpdateAction() {
771 public void perform() throws Exception
772 {
773 assertEquals(5, (int) countDocs.call());
774
775 lcon.withWriter(new ILuceneConnection.WriterAction()
776 {
777 public void perform(IndexWriter writer) throws IOException
778 {
779 writer.deleteDocuments(new Term(FIELD_NAME_COUNT, "0"));
780 }
781 });
782
783 assertEquals(5, (int) countDocs.call());
784 }
785 });
786
787 assertEquals(4, (int) countDocs.call());
788 }
789
790 public void testClose() throws Exception
791 {
792 lcon.close();
793 Method[] methods = LuceneConnection.class.getDeclaredMethods();
794 for (Method method : methods)
795 {
796 if (!Modifier.isPublic(method.getModifiers()))
797 continue;
798
799 try
800 {
801 invokeWithDefaultParams(method);
802 fail(method + " should throw a " + LuceneConnectionClosedException.class.getName() + " because the connection is closed");
803 }
804 catch (InvocationTargetException e)
805 {
806 assertEquals(method + " didn't throw the correct exception", LuceneConnectionClosedException.class,
807 e.getTargetException().getClass());
808 }
809 catch (IllegalArgumentException e)
810 {
811 throw new RuntimeException("Could not execute method: " + method);
812 }
813 }
814 }
815
816 private void invokeWithDefaultParams(Method method) throws IllegalAccessException, InvocationTargetException
817 {
818 List<Object> parameters = new ArrayList<Object>();
819 Class<?>[] parameterTypes = method.getParameterTypes();
820 for (Class<?> paramType : parameterTypes)
821 {
822 if (paramType.equals(Integer.TYPE))
823 parameters.add(0);
824 else
825 parameters.add(null);
826 }
827 method.invoke(lcon, parameters.toArray(new Object[parameters.size()]));
828 }
829
830 public void testCantCloseTwice() throws Exception
831 {
832 lcon.close();
833 try
834 {
835 lcon.close();
836 fail("Should throw a " + LuceneConnectionClosedException.class.getName() + " because the connection is closed");
837 }
838 catch (LuceneConnectionClosedException expected) {}
839 }
840
841 public void testTruncateIndex() throws Exception
842 {
843 final int docs = 100;
844
845 lcon.withWriter(new ILuceneConnection.WriterAction() {
846 public void perform(IndexWriter writer) throws IOException
847 {
848 for (int i = 0; i < docs; i++)
849 {
850 writer.addDocument(createDocument());
851 }
852 }
853 });
854 assertEquals(docs, lcon.getNumDocs());
855
856 lcon.truncateIndex();
857
858 assertEquals(0, lcon.getNumDocs());
859 }
860
861 public void testTruncateIndexWithDeletes() throws Exception
862 {
863 lcon.withWriter(new ILuceneConnection.WriterAction() {
864 public void perform(IndexWriter writer) throws IOException
865 {
866 writer.addDocument(createDocument("0"));
867 writer.addDocument(createDocument("1"));
868 }
869 });
870 lcon.withWriter(new ILuceneConnection.WriterAction()
871 {
872 public void perform(IndexWriter writer) throws IOException
873 {
874 writer.deleteDocuments(new Term(FIELD_NAME_COUNT, "0"));
875 }
876 });
877 lcon.withWriter(new ILuceneConnection.WriterAction() {
878 public void perform(IndexWriter writer) throws IOException
879 {
880 writer.addDocument(createDocument());
881 }
882 });
883
884 assertEquals(2, lcon.getNumDocs());
885
886 lcon.truncateIndex();
887
888 assertEquals(0, lcon.getNumDocs());
889 }
890
891 public void testTruncateIndexDoesNotAffectCurrentReaders() throws Exception
892 {
893 final int docs = 3;
894
895 lcon.withWriter(new ILuceneConnection.WriterAction() {
896 public void perform(IndexWriter writer) throws IOException
897 {
898 for (int i = 0; i < docs; i++)
899 {
900 writer.addDocument(createDocument());
901 }
902 }
903 });
904 assertEquals(docs, lcon.getNumDocs());
905
906 final AtomicReference<IndexReader> readerRef = new AtomicReference<IndexReader>();
907 lcon.withReader(new ILuceneConnection.ReaderAction() {
908 public Object perform(IndexReader reader)
909 {
910 readerRef.set(reader);
911 return null;
912 }
913 });
914
915 assertEquals("check document count before truncate", docs, readerRef.get().numDocs());
916
917 lcon.truncateIndex();
918
919 assertEquals("existing readers should not see truncate", docs, readerRef.get().numDocs());
920
921 assertEquals(0, lcon.getNumDocs());
922 lcon.withReader(new ILuceneConnection.ReaderAction() {
923 public Object perform(IndexReader reader)
924 {
925 assertNotSame("reader should have been refreshed after truncate", readerRef.get(), reader);
926 assertEquals(0, reader.numDocs());
927 return null;
928 }
929 });
930 }
931
932 public void testSearcherInitialisation() throws Exception
933 {
934 final AtomicBoolean isSearcherInitialised = new AtomicBoolean();
935 lcon = new LuceneConnection(directory, new StandardAnalyzer(), ILuceneConnection.DEFAULT_CONFIGURATION, new SearcherInitialisation() {
936 public void initialise(IndexSearcher searcher)
937 {
938 isSearcherInitialised.set(true);
939 }
940 });
941
942
943 lcon.withWriter(new ILuceneConnection.WriterAction()
944 {
945 public void perform(IndexWriter writer) throws IOException
946 {
947 for (int i = 0; i < ILuceneConnection.DEFAULT_CONFIGURATION.getInteractiveMaxMergeDocs(); i++)
948 {
949 writer.addDocument(createDocument());
950 }
951 writer.addDocument(createDocument());
952 }
953 });
954
955 lcon.withReader(new ILuceneConnection.ReaderAction() {
956 public Object perform(IndexReader reader)
957 {
958 assertTrue(isSearcherInitialised.get());
959 return null;
960 }
961 });
962
963
964 isSearcherInitialised.set(false);
965
966 lcon.withWriter(new ILuceneConnection.WriterAction() {
967 public void perform(IndexWriter writer) throws IOException
968 {
969 assertFalse(isSearcherInitialised.get());
970 for (int i = 0; i < ILuceneConnection.DEFAULT_CONFIGURATION.getInteractiveMaxMergeDocs(); i++)
971 {
972 writer.addDocument(createDocument());
973 }
974 writer.addDocument(createDocument());
975 }
976 });
977
978 assertTrue(isSearcherInitialised.get());
979 }
980
981
982 public void testWriteDuringWithSearch() throws Exception
983 {
984 final CountDownLatch readerStarted = new CountDownLatch(1);
985 final CountDownLatch readerLatch = new CountDownLatch(1);
986
987 new Thread(new Runnable() {
988 public void run()
989 {
990 try
991 {
992 readerStarted.await(1, SECONDS);
993 }
994 catch (InterruptedException e)
995 {
996 throw new RuntimeException(e);
997 }
998 lcon.withWriter(NOOP_WRITER_ACTION);
999 readerLatch.countDown();
1000 }
1001 }).start();
1002
1003 lcon.withSearch(new ILuceneConnection.SearcherAction()
1004 {
1005 public void perform(IndexSearcher searcher)
1006 {
1007 readerStarted.countDown();
1008 try
1009 {
1010 readerLatch.await(1, SECONDS);
1011 }
1012 catch (InterruptedException e)
1013 {
1014 throw new RuntimeException(e);
1015 }
1016 }
1017 });
1018 }
1019
1020 public void testWriteDuringWithReader() throws Exception
1021 {
1022 final CountDownLatch readerStarted = new CountDownLatch(1);
1023 final CountDownLatch readerLatch = new CountDownLatch(1);
1024
1025 new Thread(new Runnable() {
1026 public void run()
1027 {
1028 try
1029 {
1030 readerStarted.await(1, SECONDS);
1031 }
1032 catch (InterruptedException e)
1033 {
1034 throw new RuntimeException(e);
1035 }
1036 lcon.withWriter(NOOP_WRITER_ACTION);
1037 readerLatch.countDown();
1038 }
1039 }).start();
1040
1041 lcon.withReader(new ILuceneConnection.ReaderAction()
1042 {
1043 public Object perform(IndexReader reader)
1044 {
1045 readerStarted.countDown();
1046 try
1047 {
1048 readerLatch.await(1, SECONDS);
1049 }
1050 catch (InterruptedException e)
1051 {
1052 throw new RuntimeException(e);
1053 }
1054 return null;
1055 }
1056 });
1057 }
1058
1059 private Document createDocument(String id)
1060 {
1061 Document d = new Document();
1062 d.add(new Field(FIELD_NAME, FIELD_VALUE, Field.Store.YES, Field.Index.TOKENIZED));
1063 d.add(new Field(FIELD_NAME_COUNT, id, Field.Store.YES, Field.Index.TOKENIZED));
1064 return d;
1065 }
1066
1067 private Document createDocument()
1068 {
1069 return createDocument(UUID.randomUUID().toString());
1070 }
1071
1072 private void addDocsToIndex(final int numDocs)
1073 {
1074 lcon.withWriter(new ILuceneConnection.WriterAction()
1075 {
1076 public void perform(IndexWriter writer) throws IOException
1077 {
1078 for (int i = 0; i < numDocs; ++i)
1079 {
1080 writer.addDocument(createDocument(Integer.toString(i)));
1081 }
1082 }
1083 });
1084 }
1085 }