001: package org.apache.lucene.store;
002:
003: /**
004: * Licensed to the Apache Software Foundation (ASF) under one or more
005: * contributor license agreements. See the NOTICE file distributed with
006: * this work for additional information regarding copyright ownership.
007: * The ASF licenses this file to You under the Apache License, Version 2.0
008: * (the "License"); you may not use this file except in compliance with
009: * the License. You may obtain a copy of the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS,
015: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: * See the License for the specific language governing permissions and
017: * limitations under the License.
018: */
019:
020: import org.apache.lucene.util.LuceneTestCase;
021:
022: import java.util.Hashtable;
023: import java.util.Enumeration;
024:
025: import java.io.IOException;
026: import java.io.File;
027:
028: import org.apache.lucene.document.Document;
029: import org.apache.lucene.document.Field;
030: import org.apache.lucene.index.IndexReader;
031: import org.apache.lucene.index.IndexWriter;
032: import org.apache.lucene.search.IndexSearcher;
033: import org.apache.lucene.search.Query;
034: import org.apache.lucene.index.Term;
035: import org.apache.lucene.search.TermQuery;
036: import org.apache.lucene.search.Hits;
037: import org.apache.lucene.analysis.WhitespaceAnalyzer;
038:
039: public class TestLockFactory extends LuceneTestCase {
040:
041: // Verify: we can provide our own LockFactory implementation, the right
042: // methods are called at the right time, locks are created, etc.
043:
044: public void testCustomLockFactory() throws IOException {
045: Directory dir = new RAMDirectory();
046: MockLockFactory lf = new MockLockFactory();
047: dir.setLockFactory(lf);
048:
049: // Lock prefix should have been set:
050: assertTrue("lock prefix was not set by the RAMDirectory",
051: lf.lockPrefixSet);
052:
053: IndexWriter writer = new IndexWriter(dir,
054: new WhitespaceAnalyzer(), true);
055:
056: // add 100 documents (so that commit lock is used)
057: for (int i = 0; i < 100; i++) {
058: addDoc(writer);
059: }
060:
061: // Both write lock and commit lock should have been created:
062: assertEquals(
063: "# of unique locks created (after instantiating IndexWriter)",
064: 1, lf.locksCreated.size());
065: assertTrue(
066: "# calls to makeLock is 0 (after instantiating IndexWriter)",
067: lf.makeLockCount >= 1);
068:
069: for (Enumeration e = lf.locksCreated.keys(); e
070: .hasMoreElements();) {
071: String lockName = (String) e.nextElement();
072: MockLockFactory.MockLock lock = (MockLockFactory.MockLock) lf.locksCreated
073: .get(lockName);
074: assertTrue(
075: "# calls to Lock.obtain is 0 (after instantiating IndexWriter)",
076: lock.lockAttempts > 0);
077: }
078:
079: writer.close();
080: }
081:
082: // Verify: we can use the NoLockFactory with RAMDirectory w/ no
083: // exceptions raised:
084: // Verify: NoLockFactory allows two IndexWriters
085: public void testRAMDirectoryNoLocking() throws IOException {
086: Directory dir = new RAMDirectory();
087: dir.setLockFactory(NoLockFactory.getNoLockFactory());
088:
089: assertTrue("RAMDirectory.setLockFactory did not take",
090: NoLockFactory.class.isInstance(dir.getLockFactory()));
091:
092: IndexWriter writer = new IndexWriter(dir,
093: new WhitespaceAnalyzer(), true);
094:
095: // Create a 2nd IndexWriter. This is normally not allowed but it should run through since we're not
096: // using any locks:
097: IndexWriter writer2 = null;
098: try {
099: writer2 = new IndexWriter(dir, new WhitespaceAnalyzer(),
100: false);
101: } catch (Exception e) {
102: e.printStackTrace(System.out);
103: fail("Should not have hit an IOException with no locking");
104: }
105:
106: writer.close();
107: if (writer2 != null) {
108: writer2.close();
109: }
110: }
111:
112: // Verify: SingleInstanceLockFactory is the default lock for RAMDirectory
113: // Verify: RAMDirectory does basic locking correctly (can't create two IndexWriters)
114: public void testDefaultRAMDirectory() throws IOException {
115: Directory dir = new RAMDirectory();
116:
117: assertTrue("RAMDirectory did not use correct LockFactory: got "
118: + dir.getLockFactory(), SingleInstanceLockFactory.class
119: .isInstance(dir.getLockFactory()));
120:
121: IndexWriter writer = new IndexWriter(dir,
122: new WhitespaceAnalyzer(), true);
123:
124: // Create a 2nd IndexWriter. This should fail:
125: IndexWriter writer2 = null;
126: try {
127: writer2 = new IndexWriter(dir, new WhitespaceAnalyzer(),
128: false);
129: fail("Should have hit an IOException with two IndexWriters on default SingleInstanceLockFactory");
130: } catch (IOException e) {
131: }
132:
133: writer.close();
134: if (writer2 != null) {
135: writer2.close();
136: }
137: }
138:
139: // Verify: SimpleFSLockFactory is the default for FSDirectory
140: // Verify: FSDirectory does basic locking correctly
141: public void testDefaultFSDirectory() throws IOException {
142: String indexDirName = "index.TestLockFactory1";
143:
144: IndexWriter writer = new IndexWriter(indexDirName,
145: new WhitespaceAnalyzer(), true);
146:
147: assertTrue("FSDirectory did not use correct LockFactory: got "
148: + writer.getDirectory().getLockFactory(),
149: SimpleFSLockFactory.class.isInstance(writer
150: .getDirectory().getLockFactory())
151: || NativeFSLockFactory.class.isInstance(writer
152: .getDirectory().getLockFactory()));
153:
154: IndexWriter writer2 = null;
155:
156: // Create a 2nd IndexWriter. This should fail:
157: try {
158: writer2 = new IndexWriter(indexDirName,
159: new WhitespaceAnalyzer(), false);
160: fail("Should have hit an IOException with two IndexWriters on default SimpleFSLockFactory");
161: } catch (IOException e) {
162: }
163:
164: writer.close();
165: if (writer2 != null) {
166: writer2.close();
167: }
168:
169: // Cleanup
170: rmDir(indexDirName);
171: }
172:
173: // Verify: FSDirectory's default lockFactory clears all locks correctly
174: public void testFSDirectoryTwoCreates() throws IOException {
175: String indexDirName = "index.TestLockFactory2";
176:
177: IndexWriter writer = new IndexWriter(indexDirName,
178: new WhitespaceAnalyzer(), true);
179:
180: assertTrue("FSDirectory did not use correct LockFactory: got "
181: + writer.getDirectory().getLockFactory(),
182: SimpleFSLockFactory.class.isInstance(writer
183: .getDirectory().getLockFactory())
184: || NativeFSLockFactory.class.isInstance(writer
185: .getDirectory().getLockFactory()));
186:
187: // Intentionally do not close the first writer here.
188: // The goal is to "simulate" a crashed writer and
189: // ensure the second writer, with create=true, is
190: // able to remove the lock files. This works OK
191: // with SimpleFSLockFactory as the locking
192: // implementation. Note, however, that this test
193: // will not work on WIN32 when we switch to
194: // NativeFSLockFactory as the default locking for
195: // FSDirectory because the second IndexWriter cannot
196: // remove those lock files since they are held open
197: // by the first writer. This is because leaving the
198: // first IndexWriter open is not really a good way
199: // to simulate a crashed writer.
200:
201: // Create a 2nd IndexWriter. This should not fail:
202: IndexWriter writer2 = null;
203: try {
204: writer2 = new IndexWriter(indexDirName,
205: new WhitespaceAnalyzer(), true);
206: } catch (IOException e) {
207: e.printStackTrace(System.out);
208: fail("Should not have hit an IOException with two IndexWriters with create=true, on default SimpleFSLockFactory");
209: }
210:
211: writer.close();
212: if (writer2 != null) {
213: try {
214: writer2.close();
215: // expected
216: } catch (LockReleaseFailedException e) {
217: fail("writer2.close() should not have hit LockReleaseFailedException");
218: }
219: }
220:
221: // Cleanup
222: rmDir(indexDirName);
223: }
224:
225: // Verify: setting custom lock factory class (as system property) works:
226: // Verify: all 4 builtin LockFactory implementations are
227: // settable this way
228: // Verify: FSDirectory does basic locking correctly
229: public void testLockClassProperty() throws IOException {
230: String indexDirName = "index.TestLockFactory3";
231: String prpName = "org.apache.lucene.store.FSDirectoryLockFactoryClass";
232:
233: try {
234:
235: // NoLockFactory:
236: System.setProperty(prpName,
237: "org.apache.lucene.store.NoLockFactory");
238: IndexWriter writer = new IndexWriter(indexDirName,
239: new WhitespaceAnalyzer(), true);
240: assertTrue(
241: "FSDirectory did not use correct LockFactory: got "
242: + writer.getDirectory().getLockFactory(),
243: NoLockFactory.class.isInstance(writer
244: .getDirectory().getLockFactory()));
245: writer.close();
246:
247: // SingleInstanceLockFactory:
248: System
249: .setProperty(prpName,
250: "org.apache.lucene.store.SingleInstanceLockFactory");
251: writer = new IndexWriter(indexDirName,
252: new WhitespaceAnalyzer(), true);
253: assertTrue(
254: "FSDirectory did not use correct LockFactory: got "
255: + writer.getDirectory().getLockFactory(),
256: SingleInstanceLockFactory.class.isInstance(writer
257: .getDirectory().getLockFactory()));
258: writer.close();
259:
260: // NativeFSLockFactory:
261: System.setProperty(prpName,
262: "org.apache.lucene.store.NativeFSLockFactory");
263: writer = new IndexWriter(indexDirName,
264: new WhitespaceAnalyzer(), true);
265: assertTrue(
266: "FSDirectory did not use correct LockFactory: got "
267: + writer.getDirectory().getLockFactory(),
268: NativeFSLockFactory.class.isInstance(writer
269: .getDirectory().getLockFactory()));
270: writer.close();
271:
272: // SimpleFSLockFactory:
273: System.setProperty(prpName,
274: "org.apache.lucene.store.SimpleFSLockFactory");
275: writer = new IndexWriter(indexDirName,
276: new WhitespaceAnalyzer(), true);
277: assertTrue(
278: "FSDirectory did not use correct LockFactory: got "
279: + writer.getDirectory().getLockFactory(),
280: SimpleFSLockFactory.class.isInstance(writer
281: .getDirectory().getLockFactory()));
282: writer.close();
283: } finally {
284: // Put back to the correct default for subsequent tests:
285: System
286: .setProperty(
287: "org.apache.lucene.store.FSDirectoryLockFactoryClass",
288: "");
289: }
290:
291: // Cleanup
292: rmDir(indexDirName);
293: }
294:
295: // Verify: setDisableLocks works
296: public void testDisableLocks() throws IOException {
297: String indexDirName = "index.TestLockFactory4";
298:
299: assertTrue("Locks are already disabled", !FSDirectory
300: .getDisableLocks());
301: FSDirectory.setDisableLocks(true);
302:
303: IndexWriter writer = new IndexWriter(indexDirName,
304: new WhitespaceAnalyzer(), true);
305:
306: assertTrue(
307: "FSDirectory did not use correct default LockFactory: got "
308: + writer.getDirectory().getLockFactory(),
309: NoLockFactory.class.isInstance(writer.getDirectory()
310: .getLockFactory()));
311:
312: // Should be no error since locking is disabled:
313: IndexWriter writer2 = null;
314: try {
315: writer2 = new IndexWriter(indexDirName,
316: new WhitespaceAnalyzer(), false);
317: } catch (IOException e) {
318: e.printStackTrace(System.out);
319: fail("Should not have hit an IOException with locking disabled");
320: }
321:
322: FSDirectory.setDisableLocks(false);
323: writer.close();
324: if (writer2 != null) {
325: writer2.close();
326: }
327: // Cleanup
328: rmDir(indexDirName);
329: }
330:
331: // Verify: if I try to getDirectory() with two different locking implementations, I get an IOException
332: public void testFSDirectoryDifferentLockFactory()
333: throws IOException {
334: String indexDirName = "index.TestLockFactory5";
335:
336: LockFactory lf = new SingleInstanceLockFactory();
337: FSDirectory fs1 = FSDirectory.getDirectory(indexDirName, lf);
338:
339: // Different lock factory instance should hit IOException:
340: try {
341: FSDirectory fs2 = FSDirectory.getDirectory(indexDirName,
342: new SingleInstanceLockFactory());
343: fail("Should have hit an IOException because LockFactory instances differ");
344: } catch (IOException e) {
345: }
346:
347: FSDirectory fs2 = null;
348:
349: // Same lock factory instance should not:
350: try {
351: fs2 = FSDirectory.getDirectory(indexDirName, lf);
352: } catch (IOException e) {
353: e.printStackTrace(System.out);
354: fail("Should not have hit an IOException because LockFactory instances are the same");
355: }
356:
357: fs1.close();
358: if (fs2 != null) {
359: fs2.close();
360: }
361: // Cleanup
362: rmDir(indexDirName);
363: }
364:
365: // Verify: do stress test, by opening IndexReaders and
366: // IndexWriters over & over in 2 threads and making sure
367: // no unexpected exceptions are raised:
368: public void testStressLocks() throws IOException {
369: _testStressLocks(null, "index.TestLockFactory6");
370: }
371:
372: // Verify: do stress test, by opening IndexReaders and
373: // IndexWriters over & over in 2 threads and making sure
374: // no unexpected exceptions are raised, but use
375: // NativeFSLockFactory:
376: public void testStressLocksNativeFSLockFactory() throws IOException {
377: _testStressLocks(new NativeFSLockFactory(
378: "index.TestLockFactory7"), "index.TestLockFactory7");
379: }
380:
381: public void _testStressLocks(LockFactory lockFactory,
382: String indexDirName) throws IOException {
383: FSDirectory fs1 = FSDirectory.getDirectory(indexDirName,
384: lockFactory);
385:
386: // First create a 1 doc index:
387: IndexWriter w = new IndexWriter(fs1, new WhitespaceAnalyzer(),
388: true);
389: addDoc(w);
390: w.close();
391:
392: WriterThread writer = new WriterThread(100, fs1);
393: SearcherThread searcher = new SearcherThread(100, fs1);
394: writer.start();
395: searcher.start();
396:
397: while (writer.isAlive() || searcher.isAlive()) {
398: try {
399: Thread.sleep(1000);
400: } catch (InterruptedException e) {
401: }
402: }
403:
404: assertTrue("IndexWriter hit unexpected exceptions",
405: !writer.hitException);
406: assertTrue("IndexSearcher hit unexpected exceptions",
407: !searcher.hitException);
408:
409: // Cleanup
410: rmDir(indexDirName);
411: }
412:
413: // Verify: NativeFSLockFactory works correctly
414: public void testNativeFSLockFactory() throws IOException {
415:
416: NativeFSLockFactory f = new NativeFSLockFactory(System
417: .getProperty("tempDir"));
418:
419: NativeFSLockFactory f2 = new NativeFSLockFactory(System
420: .getProperty("tempDir"));
421:
422: f.setLockPrefix("test");
423: Lock l = f.makeLock("commit");
424: Lock l2 = f.makeLock("commit");
425:
426: assertTrue("failed to obtain lock", l.obtain());
427: assertTrue("succeeded in obtaining lock twice", !l2.obtain());
428: l.release();
429:
430: assertTrue(
431: "failed to obtain 2nd lock after first one was freed",
432: l2.obtain());
433: l2.release();
434:
435: // Make sure we can obtain first one again:
436: assertTrue("failed to obtain lock", l.obtain());
437: l.release();
438: }
439:
440: // Verify: NativeFSLockFactory assigns different lock
441: // prefixes to different directories:
442: public void testNativeFSLockFactoryPrefix() throws IOException {
443:
444: // Make sure we get identical instances:
445: Directory dir1 = FSDirectory.getDirectory("TestLockFactory.8",
446: new NativeFSLockFactory("TestLockFactory.8"));
447: Directory dir2 = FSDirectory.getDirectory("TestLockFactory.9",
448: new NativeFSLockFactory("TestLockFactory.9"));
449:
450: String prefix1 = dir1.getLockFactory().getLockPrefix();
451: String prefix2 = dir2.getLockFactory().getLockPrefix();
452:
453: assertTrue(
454: "Native Lock Factories are incorrectly shared: dir1 and dir2 have same lock prefix '"
455: + prefix1 + "'; they should be different",
456: !prefix1.equals(prefix2));
457: rmDir("TestLockFactory.8");
458: rmDir("TestLockFactory.9");
459: }
460:
461: // Verify: default LockFactory has no prefix (ie
462: // write.lock is stored in index):
463: public void testDefaultFSLockFactoryPrefix() throws IOException {
464:
465: // Make sure we get null prefix:
466: Directory dir = FSDirectory.getDirectory("TestLockFactory.10");
467:
468: String prefix = dir.getLockFactory().getLockPrefix();
469:
470: assertTrue("Default lock prefix should be null", null == prefix);
471:
472: rmDir("TestLockFactory.10");
473: }
474:
475: private class WriterThread extends Thread {
476: private Directory dir;
477: private int numIteration;
478: public boolean hitException = false;
479:
480: public WriterThread(int numIteration, Directory dir) {
481: this .numIteration = numIteration;
482: this .dir = dir;
483: }
484:
485: public void run() {
486: WhitespaceAnalyzer analyzer = new WhitespaceAnalyzer();
487: IndexWriter writer = null;
488: for (int i = 0; i < this .numIteration; i++) {
489: try {
490: writer = new IndexWriter(dir, analyzer, false);
491: } catch (IOException e) {
492: if (e.toString().indexOf(" timed out:") == -1) {
493: hitException = true;
494: } else {
495: // lock obtain timed out
496: // NOTE: we should at some point
497: // consider this a failure? The lock
498: // obtains, across IndexReader &
499: // IndexWriters should be "fair" (ie
500: // FIFO).
501: }
502: } catch (Exception e) {
503: hitException = true;
504: System.out
505: .println("Stress Test Index Writer: creation hit unexpected exception: "
506: + e.toString());
507: e.printStackTrace(System.out);
508: break;
509: }
510: if (writer != null) {
511: try {
512: addDoc(writer);
513: } catch (IOException e) {
514: hitException = true;
515: System.out
516: .println("Stress Test Index Writer: addDoc hit unexpected exception: "
517: + e.toString());
518: e.printStackTrace(System.out);
519: break;
520: }
521: try {
522: writer.close();
523: } catch (IOException e) {
524: hitException = true;
525: System.out
526: .println("Stress Test Index Writer: close hit unexpected exception: "
527: + e.toString());
528: e.printStackTrace(System.out);
529: break;
530: }
531: writer = null;
532: }
533: }
534: }
535: }
536:
537: private class SearcherThread extends Thread {
538: private Directory dir;
539: private int numIteration;
540: public boolean hitException = false;
541:
542: public SearcherThread(int numIteration, Directory dir) {
543: this .numIteration = numIteration;
544: this .dir = dir;
545: }
546:
547: public void run() {
548: IndexSearcher searcher = null;
549: WhitespaceAnalyzer analyzer = new WhitespaceAnalyzer();
550: Query query = new TermQuery(new Term("content", "aaa"));
551: for (int i = 0; i < this .numIteration; i++) {
552: try {
553: searcher = new IndexSearcher(dir);
554: } catch (Exception e) {
555: hitException = true;
556: System.out
557: .println("Stress Test Index Searcher: create hit unexpected exception: "
558: + e.toString());
559: e.printStackTrace(System.out);
560: break;
561: }
562: if (searcher != null) {
563: Hits hits = null;
564: try {
565: hits = searcher.search(query);
566: } catch (IOException e) {
567: hitException = true;
568: System.out
569: .println("Stress Test Index Searcher: search hit unexpected exception: "
570: + e.toString());
571: e.printStackTrace(System.out);
572: break;
573: }
574: // System.out.println(hits.length() + " total results");
575: try {
576: searcher.close();
577: } catch (IOException e) {
578: hitException = true;
579: System.out
580: .println("Stress Test Index Searcher: close hit unexpected exception: "
581: + e.toString());
582: e.printStackTrace(System.out);
583: break;
584: }
585: searcher = null;
586: }
587: }
588: }
589: }
590:
591: public class MockLockFactory extends LockFactory {
592:
593: public boolean lockPrefixSet;
594: public Hashtable locksCreated = new Hashtable();
595: public int makeLockCount = 0;
596:
597: public void setLockPrefix(String lockPrefix) {
598: super .setLockPrefix(lockPrefix);
599: lockPrefixSet = true;
600: }
601:
602: synchronized public Lock makeLock(String lockName) {
603: Lock lock = new MockLock();
604: locksCreated.put(lockName, lock);
605: makeLockCount++;
606: return lock;
607: }
608:
609: public void clearLock(String specificLockName) {
610: }
611:
612: public class MockLock extends Lock {
613: public int lockAttempts;
614:
615: public boolean obtain() {
616: lockAttempts++;
617: return true;
618: }
619:
620: public void release() {
621: // do nothing
622: }
623:
624: public boolean isLocked() {
625: return false;
626: }
627: }
628: }
629:
630: private void addDoc(IndexWriter writer) throws IOException {
631: Document doc = new Document();
632: doc.add(new Field("content", "aaa", Field.Store.NO,
633: Field.Index.TOKENIZED));
634: writer.addDocument(doc);
635: }
636:
637: private void rmDir(String dirName) {
638: File dir = new java.io.File(dirName);
639: String[] files = dir.list(); // clear old files
640: for (int i = 0; i < files.length; i++) {
641: File file = new File(dir, files[i]);
642: file.delete();
643: }
644: dir.delete();
645: }
646: }
|