~slub.team/goobi-indexserver/3.x

« back to all changes in this revision

Viewing changes to lucene/src/java/org/apache/lucene/index/IndexWriter.java

  • Committer: Sebastian Meyer
  • Date: 2012-08-03 09:12:40 UTC
  • Revision ID: sebastian.meyer@slub-dresden.de-20120803091240-x6861b0vabq1xror
Remove Lucene and Solr source code and add patches instead
Fix Bug #985487: Auto-suggestion for the search interface

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
package org.apache.lucene.index;
2
 
 
3
 
/**
4
 
 * Licensed to the Apache Software Foundation (ASF) under one or more
5
 
 * contributor license agreements.  See the NOTICE file distributed with
6
 
 * this work for additional information regarding copyright ownership.
7
 
 * The ASF licenses this file to You under the Apache License, Version 2.0
8
 
 * (the "License"); you may not use this file except in compliance with
9
 
 * the License.  You may obtain a copy of the License at
10
 
 *
11
 
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 
 *
13
 
 * Unless required by applicable law or agreed to in writing, software
14
 
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 
 * See the License for the specific language governing permissions and
17
 
 * limitations under the License.
18
 
 */
19
 
 
20
 
import java.io.Closeable;
21
 
import java.io.IOException;
22
 
import java.io.PrintStream;
23
 
import java.util.ArrayList;
24
 
import java.util.Collection;
25
 
import java.util.Collections;
26
 
import java.util.Comparator;
27
 
import java.util.Date;
28
 
import java.util.HashMap;
29
 
import java.util.HashSet;
30
 
import java.util.LinkedList;
31
 
import java.util.List;
32
 
import java.util.Map;
33
 
import java.util.Set;
34
 
import java.util.concurrent.atomic.AtomicInteger;
35
 
import java.util.concurrent.ConcurrentHashMap;
36
 
 
37
 
import org.apache.lucene.analysis.Analyzer;
38
 
import org.apache.lucene.analysis.LimitTokenCountAnalyzer;
39
 
import org.apache.lucene.document.Document;
40
 
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
41
 
import org.apache.lucene.index.PayloadProcessorProvider.DirPayloadProcessor;
42
 
import org.apache.lucene.search.Similarity;
43
 
import org.apache.lucene.search.Query;
44
 
import org.apache.lucene.store.AlreadyClosedException;
45
 
import org.apache.lucene.store.BufferedIndexInput;
46
 
import org.apache.lucene.store.Directory;
47
 
import org.apache.lucene.store.Lock;
48
 
import org.apache.lucene.store.LockObtainFailedException;
49
 
import org.apache.lucene.util.Constants;
50
 
import org.apache.lucene.util.StringHelper;
51
 
import org.apache.lucene.util.ThreadInterruptedException;
52
 
import org.apache.lucene.util.Version;
53
 
import org.apache.lucene.util.MapBackedSet;
54
 
import org.apache.lucene.util.TwoPhaseCommit;
55
 
 
56
 
/**
57
 
  An <code>IndexWriter</code> creates and maintains an index.
58
 
 
59
 
  <p>The <code>create</code> argument to the {@link
60
 
  #IndexWriter(Directory, Analyzer, boolean, MaxFieldLength) constructor} determines 
61
 
  whether a new index is created, or whether an existing index is
62
 
  opened.  Note that you can open an index with <code>create=true</code>
63
 
  even while readers are using the index.  The old readers will 
64
 
  continue to search the "point in time" snapshot they had opened, 
65
 
  and won't see the newly created index until they re-open.  There are
66
 
  also {@link #IndexWriter(Directory, Analyzer, MaxFieldLength) constructors}
67
 
  with no <code>create</code> argument which will create a new index
68
 
  if there is not already an index at the provided path and otherwise 
69
 
  open the existing index.</p>
70
 
 
71
 
  <p>In either case, documents are added with {@link #addDocument(Document)
72
 
  addDocument} and removed with {@link #deleteDocuments(Term)} or {@link
73
 
  #deleteDocuments(Query)}. A document can be updated with {@link
74
 
  #updateDocument(Term, Document) updateDocument} (which just deletes
75
 
  and then adds the entire document). When finished adding, deleting 
76
 
  and updating documents, {@link #close() close} should be called.</p>
77
 
 
78
 
  <a name="flush"></a>
79
 
  <p>These changes are buffered in memory and periodically
80
 
  flushed to the {@link Directory} (during the above method
81
 
  calls).  A flush is triggered when there are enough
82
 
  buffered deletes (see {@link #setMaxBufferedDeleteTerms})
83
 
  or enough added documents since the last flush, whichever
84
 
  is sooner.  For the added documents, flushing is triggered
85
 
  either by RAM usage of the documents (see {@link
86
 
  #setRAMBufferSizeMB}) or the number of added documents.
87
 
  The default is to flush when RAM usage hits 16 MB.  For
88
 
  best indexing speed you should flush by RAM usage with a
89
 
  large RAM buffer.  Note that flushing just moves the
90
 
  internal buffered state in IndexWriter into the index, but
91
 
  these changes are not visible to IndexReader until either
92
 
  {@link #commit()} or {@link #close} is called.  A flush may
93
 
  also trigger one or more segment merges which by default
94
 
  run with a background thread so as not to block the
95
 
  addDocument calls (see <a href="#mergePolicy">below</a>
96
 
  for changing the {@link MergeScheduler}).</p>
97
 
 
98
 
  <p>Opening an <code>IndexWriter</code> creates a lock file for the directory in use. Trying to open
99
 
  another <code>IndexWriter</code> on the same directory will lead to a
100
 
  {@link LockObtainFailedException}. The {@link LockObtainFailedException}
101
 
  is also thrown if an IndexReader on the same directory is used to delete documents
102
 
  from the index.</p>
103
 
  
104
 
  <a name="deletionPolicy"></a>
105
 
  <p>Expert: <code>IndexWriter</code> allows an optional
106
 
  {@link IndexDeletionPolicy} implementation to be
107
 
  specified.  You can use this to control when prior commits
108
 
  are deleted from the index.  The default policy is {@link
109
 
  KeepOnlyLastCommitDeletionPolicy} which removes all prior
110
 
  commits as soon as a new commit is done (this matches
111
 
  behavior before 2.2).  Creating your own policy can allow
112
 
  you to explicitly keep previous "point in time" commits
113
 
  alive in the index for some time, to allow readers to
114
 
  refresh to the new commit without having the old commit
115
 
  deleted out from under them.  This is necessary on
116
 
  filesystems like NFS that do not support "delete on last
117
 
  close" semantics, which Lucene's "point in time" search
118
 
  normally relies on. </p>
119
 
 
120
 
  <a name="mergePolicy"></a> <p>Expert:
121
 
  <code>IndexWriter</code> allows you to separately change
122
 
  the {@link MergePolicy} and the {@link MergeScheduler}.
123
 
  The {@link MergePolicy} is invoked whenever there are
124
 
  changes to the segments in the index.  Its role is to
125
 
  select which merges to do, if any, and return a {@link
126
 
  MergePolicy.MergeSpecification} describing the merges.
127
 
  The default is {@link LogByteSizeMergePolicy}.  Then, the {@link
128
 
  MergeScheduler} is invoked with the requested merges and
129
 
  it decides when and how to run the merges.  The default is
130
 
  {@link ConcurrentMergeScheduler}. </p>
131
 
 
132
 
  <a name="OOME"></a><p><b>NOTE</b>: if you hit an
133
 
  OutOfMemoryError then IndexWriter will quietly record this
134
 
  fact and block all future segment commits.  This is a
135
 
  defensive measure in case any internal state (buffered
136
 
  documents and deletions) were corrupted.  Any subsequent
137
 
  calls to {@link #commit()} will throw an
138
 
  IllegalStateException.  The only course of action is to
139
 
  call {@link #close()}, which internally will call {@link
140
 
  #rollback()}, to undo any changes to the index since the
141
 
  last commit.  You can also just call {@link #rollback()}
142
 
  directly.</p>
143
 
 
144
 
  <a name="thread-safety"></a><p><b>NOTE</b>: {@link
145
 
  IndexWriter} instances are completely thread
146
 
  safe, meaning multiple threads can call any of its
147
 
  methods, concurrently.  If your application requires
148
 
  external synchronization, you should <b>not</b>
149
 
  synchronize on the <code>IndexWriter</code> instance as
150
 
  this may cause deadlock; use your own (non-Lucene) objects
151
 
  instead. </p>
152
 
  
153
 
  <p><b>NOTE</b>: If you call
154
 
  <code>Thread.interrupt()</code> on a thread that's within
155
 
  IndexWriter, IndexWriter will try to catch this (eg, if
156
 
  it's in a wait() or Thread.sleep()), and will then throw
157
 
  the unchecked exception {@link ThreadInterruptedException}
158
 
  and <b>clear</b> the interrupt status on the thread.</p>
159
 
*/
160
 
 
161
 
/*
162
 
 * Clarification: Check Points (and commits)
163
 
 * IndexWriter writes new index files to the directory without writing a new segments_N
164
 
 * file which references these new files. It also means that the state of 
165
 
 * the in memory SegmentInfos object is different than the most recent
166
 
 * segments_N file written to the directory.
167
 
 * 
168
 
 * Each time the SegmentInfos is changed, and matches the (possibly 
169
 
 * modified) directory files, we have a new "check point". 
170
 
 * If the modified/new SegmentInfos is written to disk - as a new 
171
 
 * (generation of) segments_N file - this check point is also an 
172
 
 * IndexCommit.
173
 
 * 
174
 
 * A new checkpoint always replaces the previous checkpoint and 
175
 
 * becomes the new "front" of the index. This allows the IndexFileDeleter 
176
 
 * to delete files that are referenced only by stale checkpoints.
177
 
 * (files that were created since the last commit, but are no longer
178
 
 * referenced by the "front" of the index). For this, IndexFileDeleter 
179
 
 * keeps track of the last non commit checkpoint.
180
 
 */
181
 
public class IndexWriter implements Closeable, TwoPhaseCommit {
182
 
 
183
 
  /**
184
 
   * Default value for the write lock timeout (1,000).
185
 
   * @see #setDefaultWriteLockTimeout
186
 
   * @deprecated use {@link IndexWriterConfig#WRITE_LOCK_TIMEOUT} instead
187
 
   */
188
 
  @Deprecated
189
 
  public static long WRITE_LOCK_TIMEOUT = IndexWriterConfig.WRITE_LOCK_TIMEOUT;
190
 
 
191
 
  private long writeLockTimeout;
192
 
 
193
 
  /**
194
 
   * Name of the write lock in the index.
195
 
   */
196
 
  public static final String WRITE_LOCK_NAME = "write.lock";
197
 
 
198
 
  /**
199
 
   * Value to denote a flush trigger is disabled
200
 
   * @deprecated use {@link IndexWriterConfig#DISABLE_AUTO_FLUSH} instead
201
 
   */
202
 
  @Deprecated
203
 
  public final static int DISABLE_AUTO_FLUSH = IndexWriterConfig.DISABLE_AUTO_FLUSH;
204
 
 
205
 
  /**
206
 
   * Disabled by default (because IndexWriter flushes by RAM usage
207
 
   * by default). Change using {@link #setMaxBufferedDocs(int)}.
208
 
   * @deprecated use {@link IndexWriterConfig#DEFAULT_MAX_BUFFERED_DOCS} instead.
209
 
   */
210
 
  @Deprecated
211
 
  public final static int DEFAULT_MAX_BUFFERED_DOCS = IndexWriterConfig.DEFAULT_MAX_BUFFERED_DOCS;
212
 
 
213
 
  /**
214
 
   * Default value is 16 MB (which means flush when buffered
215
 
   * docs consume 16 MB RAM).  Change using {@link #setRAMBufferSizeMB}.
216
 
   * @deprecated use {@link IndexWriterConfig#DEFAULT_RAM_BUFFER_SIZE_MB} instead.
217
 
   */
218
 
  @Deprecated
219
 
  public final static double DEFAULT_RAM_BUFFER_SIZE_MB = IndexWriterConfig.DEFAULT_RAM_BUFFER_SIZE_MB;
220
 
 
221
 
  /**
222
 
   * Disabled by default (because IndexWriter flushes by RAM usage
223
 
   * by default). Change using {@link #setMaxBufferedDeleteTerms(int)}.
224
 
   * @deprecated use {@link IndexWriterConfig#DEFAULT_MAX_BUFFERED_DELETE_TERMS} instead
225
 
   */
226
 
  @Deprecated
227
 
  public final static int DEFAULT_MAX_BUFFERED_DELETE_TERMS = IndexWriterConfig.DEFAULT_MAX_BUFFERED_DELETE_TERMS;
228
 
 
229
 
  /**
230
 
   * Default value is 10,000. Change using {@link #setMaxFieldLength(int)}.
231
 
   * 
232
 
   * @deprecated see {@link IndexWriterConfig}
233
 
   */
234
 
  @Deprecated
235
 
  public final static int DEFAULT_MAX_FIELD_LENGTH = MaxFieldLength.UNLIMITED.getLimit();
236
 
 
237
 
  /**
238
 
   * Default value is 128. Change using {@link #setTermIndexInterval(int)}.
239
 
   * @deprecated use {@link IndexWriterConfig#DEFAULT_TERM_INDEX_INTERVAL} instead.
240
 
   */
241
 
  @Deprecated
242
 
  public final static int DEFAULT_TERM_INDEX_INTERVAL = IndexWriterConfig.DEFAULT_TERM_INDEX_INTERVAL;
243
 
 
244
 
  /**
245
 
   * Absolute hard maximum length for a term.  If a term
246
 
   * arrives from the analyzer longer than this length, it
247
 
   * is skipped and a message is printed to infoStream, if
248
 
   * set (see {@link #setInfoStream}).
249
 
   */
250
 
  public final static int MAX_TERM_LENGTH = DocumentsWriter.MAX_TERM_LENGTH;
251
 
 
252
 
  // The normal read buffer size defaults to 1024, but
253
 
  // increasing this during merging seems to yield
254
 
  // performance gains.  However we don't want to increase
255
 
  // it too much because there are quite a few
256
 
  // BufferedIndexInputs created during merging.  See
257
 
  // LUCENE-888 for details.
258
 
  private final static int MERGE_READ_BUFFER_SIZE = 4096;
259
 
 
260
 
  // Used for printing messages
261
 
  private static final AtomicInteger MESSAGE_ID = new AtomicInteger();
262
 
  private int messageID = MESSAGE_ID.getAndIncrement();
263
 
  volatile private boolean hitOOM;
264
 
 
265
 
  private final Directory directory;  // where this index resides
266
 
  private final Analyzer analyzer;    // how to analyze text
267
 
 
268
 
  // TODO 4.0: this should be made final once the setter is out
269
 
  private /*final*/Similarity similarity = Similarity.getDefault(); // how to normalize
270
 
 
271
 
  private volatile long changeCount; // increments every time a change is completed
272
 
  private long lastCommitChangeCount; // last changeCount that was committed
273
 
 
274
 
  private List<SegmentInfo> rollbackSegments;      // list of segmentInfo we will fallback to if the commit fails
275
 
 
276
 
  volatile SegmentInfos pendingCommit;            // set when a commit is pending (after prepareCommit() & before commit())
277
 
  volatile long pendingCommitChangeCount;
278
 
 
279
 
  final SegmentInfos segmentInfos = new SegmentInfos();       // the segments
280
 
 
281
 
  private DocumentsWriter docWriter;
282
 
  private IndexFileDeleter deleter;
283
 
 
284
 
  // used by forceMerge to note those needing merging
285
 
  private Map<SegmentInfo,Boolean> segmentsToMerge = new HashMap<SegmentInfo,Boolean>();
286
 
  private int mergeMaxNumSegments;
287
 
 
288
 
  private Lock writeLock;
289
 
 
290
 
  private volatile boolean closed;
291
 
  private volatile boolean closing;
292
 
 
293
 
  // Holds all SegmentInfo instances currently involved in
294
 
  // merges
295
 
  private HashSet<SegmentInfo> mergingSegments = new HashSet<SegmentInfo>();
296
 
 
297
 
  private MergePolicy mergePolicy;
298
 
  // TODO 4.0: this should be made final once the setter is removed
299
 
  private /*final*/MergeScheduler mergeScheduler;
300
 
  private LinkedList<MergePolicy.OneMerge> pendingMerges = new LinkedList<MergePolicy.OneMerge>();
301
 
  private Set<MergePolicy.OneMerge> runningMerges = new HashSet<MergePolicy.OneMerge>();
302
 
  private List<MergePolicy.OneMerge> mergeExceptions = new ArrayList<MergePolicy.OneMerge>();
303
 
  private long mergeGen;
304
 
  private boolean stopMerges;
305
 
 
306
 
  private final AtomicInteger flushCount = new AtomicInteger();
307
 
  private final AtomicInteger flushDeletesCount = new AtomicInteger();
308
 
 
309
 
  final ReaderPool readerPool = new ReaderPool();
310
 
  final BufferedDeletesStream bufferedDeletesStream;
311
 
  
312
 
  // This is a "write once" variable (like the organic dye
313
 
  // on a DVD-R that may or may not be heated by a laser and
314
 
  // then cooled to permanently record the event): it's
315
 
  // false, until getReader() is called for the first time,
316
 
  // at which point it's switched to true and never changes
317
 
  // back to false.  Once this is true, we hold open and
318
 
  // reuse SegmentReader instances internally for applying
319
 
  // deletes, doing merges, and reopening near real-time
320
 
  // readers.
321
 
  private volatile boolean poolReaders;
322
 
 
323
 
  // The instance that was passed to the constructor. It is saved only in order
324
 
  // to allow users to query an IndexWriter settings.
325
 
  private final IndexWriterConfig config;
326
 
 
327
 
  // The PayloadProcessorProvider to use when segments are merged
328
 
  private PayloadProcessorProvider payloadProcessorProvider;
329
 
 
330
 
  // for testing
331
 
  boolean anyNonBulkMerges;
332
 
 
333
 
  /**
334
 
   * Expert: returns a readonly reader, covering all
335
 
   * committed as well as un-committed changes to the index.
336
 
   * This provides "near real-time" searching, in that
337
 
   * changes made during an IndexWriter session can be
338
 
   * quickly made available for searching without closing
339
 
   * the writer nor calling {@link #commit}.
340
 
   *
341
 
   * <p>Note that this is functionally equivalent to calling
342
 
   * {#flush} and then using {@link IndexReader#open} to
343
 
   * open a new reader.  But the turarnound time of this
344
 
   * method should be faster since it avoids the potentially
345
 
   * costly {@link #commit}.</p>
346
 
   *
347
 
   * <p>You must close the {@link IndexReader} returned by
348
 
   * this method once you are done using it.</p>
349
 
   *
350
 
   * <p>It's <i>near</i> real-time because there is no hard
351
 
   * guarantee on how quickly you can get a new reader after
352
 
   * making changes with IndexWriter.  You'll have to
353
 
   * experiment in your situation to determine if it's
354
 
   * fast enough.  As this is a new and experimental
355
 
   * feature, please report back on your findings so we can
356
 
   * learn, improve and iterate.</p>
357
 
   *
358
 
   * <p>The resulting reader supports {@link
359
 
   * IndexReader#reopen}, but that call will simply forward
360
 
   * back to this method (though this may change in the
361
 
   * future).</p>
362
 
   *
363
 
   * <p>The very first time this method is called, this
364
 
   * writer instance will make every effort to pool the
365
 
   * readers that it opens for doing merges, applying
366
 
   * deletes, etc.  This means additional resources (RAM,
367
 
   * file descriptors, CPU time) will be consumed.</p>
368
 
   *
369
 
   * <p>For lower latency on reopening a reader, you should
370
 
   * call {@link #setMergedSegmentWarmer} to
371
 
   * pre-warm a newly merged segment before it's committed
372
 
   * to the index.  This is important for minimizing
373
 
   * index-to-search delay after a large merge.  </p>
374
 
   *
375
 
   * <p>If an addIndexes* call is running in another thread,
376
 
   * then this reader will only search those segments from
377
 
   * the foreign index that have been successfully copied
378
 
   * over, so far</p>.
379
 
   *
380
 
   * <p><b>NOTE</b>: Once the writer is closed, any
381
 
   * outstanding readers may continue to be used.  However,
382
 
   * if you attempt to reopen any of those readers, you'll
383
 
   * hit an {@link AlreadyClosedException}.</p>
384
 
   *
385
 
   * @lucene.experimental
386
 
   *
387
 
   * @return IndexReader that covers entire index plus all
388
 
   * changes made so far by this IndexWriter instance
389
 
   *
390
 
   * @deprecated Please use {@link
391
 
   * IndexReader#open(IndexWriter,boolean)} instead.
392
 
   *
393
 
   * @throws IOException
394
 
   */
395
 
  @Deprecated
396
 
  public IndexReader getReader() throws IOException {
397
 
    return getReader(config.getReaderTermsIndexDivisor(), true);
398
 
  }
399
 
 
400
 
  IndexReader getReader(boolean applyAllDeletes) throws IOException {
401
 
    return getReader(config.getReaderTermsIndexDivisor(), applyAllDeletes);
402
 
  }
403
 
 
404
 
  /** Expert: like {@link #getReader}, except you can
405
 
   *  specify which termInfosIndexDivisor should be used for
406
 
   *  any newly opened readers.
407
 
   * @param termInfosIndexDivisor Subsamples which indexed
408
 
   *  terms are loaded into RAM. This has the same effect as {@link
409
 
   *  IndexWriter#setTermIndexInterval} except that setting
410
 
   *  must be done at indexing time while this setting can be
411
 
   *  set per reader.  When set to N, then one in every
412
 
   *  N*termIndexInterval terms in the index is loaded into
413
 
   *  memory.  By setting this to a value > 1 you can reduce
414
 
   *  memory usage, at the expense of higher latency when
415
 
   *  loading a TermInfo.  The default value is 1.  Set this
416
 
   *  to -1 to skip loading the terms index entirely.
417
 
   *  
418
 
   *  @deprecated Please use {@link
419
 
   *  IndexReader#open(IndexWriter,boolean)} instead.  Furthermore,
420
 
   *  this method cannot guarantee the reader (and its
421
 
   *  sub-readers) will be opened with the
422
 
   *  termInfosIndexDivisor setting because some of them may
423
 
   *  have already been opened according to {@link
424
 
   *  IndexWriterConfig#setReaderTermsIndexDivisor}. You
425
 
   *  should set the requested termInfosIndexDivisor through 
426
 
   *  {@link IndexWriterConfig#setReaderTermsIndexDivisor} and use 
427
 
   *  {@link #getReader()}. */
428
 
  @Deprecated
429
 
  public IndexReader getReader(int termInfosIndexDivisor) throws IOException {
430
 
    return getReader(termInfosIndexDivisor, true);
431
 
  }
432
 
 
433
 
  IndexReader getReader(int termInfosIndexDivisor, boolean applyAllDeletes) throws IOException {
434
 
    ensureOpen();
435
 
    
436
 
    final long tStart = System.currentTimeMillis();
437
 
 
438
 
    if (infoStream != null) {
439
 
      message("flush at getReader");
440
 
    }
441
 
 
442
 
    // Do this up front before flushing so that the readers
443
 
    // obtained during this flush are pooled, the first time
444
 
    // this method is called:
445
 
    poolReaders = true;
446
 
 
447
 
    // Prevent segmentInfos from changing while opening the
448
 
    // reader; in theory we could do similar retry logic,
449
 
    // just like we do when loading segments_N
450
 
    IndexReader r;
451
 
    synchronized(this) {
452
 
      flush(false, applyAllDeletes);
453
 
      r = new ReadOnlyDirectoryReader(this, segmentInfos, termInfosIndexDivisor, applyAllDeletes);
454
 
      if (infoStream != null) {
455
 
        message("return reader version=" + r.getVersion() + " reader=" + r);
456
 
      }
457
 
    }
458
 
 
459
 
    maybeMerge();
460
 
 
461
 
    if (infoStream != null) {
462
 
      message("getReader took " + (System.currentTimeMillis() - tStart) + " msec");
463
 
    }
464
 
    return r;
465
 
  }
466
 
 
467
 
  // Used for all SegmentReaders we open
468
 
  private final Collection<IndexReader.ReaderFinishedListener> readerFinishedListeners = new MapBackedSet<IndexReader.ReaderFinishedListener>(new ConcurrentHashMap<IndexReader.ReaderFinishedListener,Boolean>());
469
 
 
470
 
  Collection<IndexReader.ReaderFinishedListener> getReaderFinishedListeners() throws IOException {
471
 
    return readerFinishedListeners;
472
 
  }
473
 
 
474
 
  /** Holds shared SegmentReader instances. IndexWriter uses
475
 
   *  SegmentReaders for 1) applying deletes, 2) doing
476
 
   *  merges, 3) handing out a real-time reader.  This pool
477
 
   *  reuses instances of the SegmentReaders in all these
478
 
   *  places if it is in "near real-time mode" (getReader()
479
 
   *  has been called on this instance). */
480
 
 
481
 
  class ReaderPool {
482
 
 
483
 
    private final Map<SegmentInfo,SegmentReader> readerMap = new HashMap<SegmentInfo,SegmentReader>();
484
 
 
485
 
    /** Forcefully clear changes for the specified segments.  This is called on successful merge. */
486
 
    synchronized void clear(List<SegmentInfo> infos) throws IOException {
487
 
      if (infos == null) {
488
 
        for (Map.Entry<SegmentInfo,SegmentReader> ent: readerMap.entrySet()) {
489
 
          ent.getValue().hasChanges = false;
490
 
        }
491
 
      } else {
492
 
        for (final SegmentInfo info: infos) {
493
 
          final SegmentReader r = readerMap.get(info);
494
 
          if (r != null) {
495
 
            r.hasChanges = false;
496
 
          }
497
 
        }     
498
 
      }
499
 
    }
500
 
    
501
 
    // used only by asserts
502
 
    public synchronized boolean infoIsLive(SegmentInfo info) {
503
 
      int idx = segmentInfos.indexOf(info);
504
 
      assert idx != -1: "info=" + info + " isn't in pool";
505
 
      assert segmentInfos.info(idx) == info: "info=" + info + " doesn't match live info in segmentInfos";
506
 
      return true;
507
 
    }
508
 
 
509
 
    public synchronized SegmentInfo mapToLive(SegmentInfo info) {
510
 
      int idx = segmentInfos.indexOf(info);
511
 
      if (idx != -1) {
512
 
        info = segmentInfos.info(idx);
513
 
      }
514
 
      return info;
515
 
    }
516
 
    
517
 
    /**
518
 
     * Release the segment reader (i.e. decRef it and close if there
519
 
     * are no more references.
520
 
     * @return true if this release altered the index (eg
521
 
     * the SegmentReader had pending changes to del docs and
522
 
     * was closed).  Caller must call checkpoint() if so.
523
 
     * @param sr
524
 
     * @throws IOException
525
 
     */
526
 
    public synchronized boolean release(SegmentReader sr) throws IOException {
527
 
      return release(sr, false);
528
 
    }
529
 
    
530
 
    /**
531
 
     * Release the segment reader (i.e. decRef it and close if there
532
 
     * are no more references.
533
 
     * @return true if this release altered the index (eg
534
 
     * the SegmentReader had pending changes to del docs and
535
 
     * was closed).  Caller must call checkpoint() if so.
536
 
     * @param sr
537
 
     * @throws IOException
538
 
     */
539
 
    public synchronized boolean release(SegmentReader sr, boolean drop) throws IOException {
540
 
 
541
 
      final boolean pooled = readerMap.containsKey(sr.getSegmentInfo());
542
 
 
543
 
      assert !pooled || readerMap.get(sr.getSegmentInfo()) == sr;
544
 
 
545
 
      // Drop caller's ref; for an external reader (not
546
 
      // pooled), this decRef will close it
547
 
      sr.decRef();
548
 
 
549
 
      if (pooled && (drop || (!poolReaders && sr.getRefCount() == 1))) {
550
 
 
551
 
        // We invoke deleter.checkpoint below, so we must be
552
 
        // sync'd on IW if there are changes:
553
 
        assert !sr.hasChanges || Thread.holdsLock(IndexWriter.this);
554
 
 
555
 
        // Discard (don't save) changes when we are dropping
556
 
        // the reader; this is used only on the sub-readers
557
 
        // after a successful merge.
558
 
        sr.hasChanges &= !drop;
559
 
 
560
 
        final boolean hasChanges = sr.hasChanges;
561
 
 
562
 
        // Drop our ref -- this will commit any pending
563
 
        // changes to the dir
564
 
        sr.close();
565
 
 
566
 
        // We are the last ref to this reader; since we're
567
 
        // not pooling readers, we release it:
568
 
        readerMap.remove(sr.getSegmentInfo());
569
 
 
570
 
        return hasChanges;
571
 
      }
572
 
 
573
 
      return false;
574
 
    }
575
 
 
576
 
    public synchronized void drop(List<SegmentInfo> infos) throws IOException {
577
 
      for(SegmentInfo info : infos) {
578
 
        drop(info);
579
 
      }
580
 
    }
581
 
 
582
 
    public synchronized void drop(SegmentInfo info) throws IOException {
583
 
      final SegmentReader sr = readerMap.get(info);
584
 
      if (sr != null) {
585
 
        sr.hasChanges = false;
586
 
        readerMap.remove(info);
587
 
        sr.close();
588
 
      }
589
 
    }
590
 
    
591
 
    public synchronized void dropAll() throws IOException {
592
 
      for(SegmentReader reader : readerMap.values()) {
593
 
        reader.hasChanges = false;
594
 
 
595
 
        // NOTE: it is allowed that this decRef does not
596
 
        // actually close the SR; this can happen when a
597
 
        // near real-time reader using this SR is still open
598
 
        reader.decRef();
599
 
      }
600
 
      readerMap.clear();
601
 
    }
602
 
 
603
 
    /** Remove all our references to readers, and commits
604
 
     *  any pending changes. */
605
 
    synchronized void close() throws IOException {
606
 
      // We invoke deleter.checkpoint below, so we must be
607
 
      // sync'd on IW:
608
 
      assert Thread.holdsLock(IndexWriter.this);
609
 
 
610
 
      for(Map.Entry<SegmentInfo,SegmentReader> ent : readerMap.entrySet()) {
611
 
        
612
 
        SegmentReader sr = ent.getValue();
613
 
        if (sr.hasChanges) {
614
 
          assert infoIsLive(sr.getSegmentInfo());
615
 
          sr.doCommit(null);
616
 
 
617
 
          // Must checkpoint w/ deleter, because this
618
 
          // segment reader will have created new _X_N.del
619
 
          // file.
620
 
          deleter.checkpoint(segmentInfos, false);
621
 
        }
622
 
 
623
 
        // NOTE: it is allowed that this decRef does not
624
 
        // actually close the SR; this can happen when a
625
 
        // near real-time reader is kept open after the
626
 
        // IndexWriter instance is closed
627
 
        sr.decRef();
628
 
      }
629
 
 
630
 
      readerMap.clear();
631
 
    }
632
 
    
633
 
    /**
634
 
     * Commit all segment reader in the pool.
635
 
     * @throws IOException
636
 
     */
637
 
    synchronized void commit(SegmentInfos infos) throws IOException {
638
 
 
639
 
      // We invoke deleter.checkpoint below, so we must be
640
 
      // sync'd on IW:
641
 
      assert Thread.holdsLock(IndexWriter.this);
642
 
 
643
 
      for (SegmentInfo info : infos) {
644
 
 
645
 
        final SegmentReader sr = readerMap.get(info);
646
 
        if (sr != null && sr.hasChanges) {
647
 
          assert infoIsLive(info);
648
 
          sr.doCommit(null);
649
 
          // Must checkpoint w/ deleter, because this
650
 
          // segment reader will have created new _X_N.del
651
 
          // file.
652
 
          deleter.checkpoint(segmentInfos, false);
653
 
        }
654
 
      }
655
 
    }
656
 
    
657
 
    /**
658
 
     * Returns a ref to a clone.  NOTE: this clone is not
659
 
     * enrolled in the pool, so you should simply close()
660
 
     * it when you're done (ie, do not call release()).
661
 
     */
662
 
    public synchronized SegmentReader getReadOnlyClone(SegmentInfo info, boolean doOpenStores, int termInfosIndexDivisor) throws IOException {
663
 
      SegmentReader sr = get(info, doOpenStores, BufferedIndexInput.BUFFER_SIZE, termInfosIndexDivisor);
664
 
      try {
665
 
        return (SegmentReader) sr.clone(true);
666
 
      } finally {
667
 
        sr.decRef();
668
 
      }
669
 
    }
670
 
   
671
 
    /**
672
 
     * Obtain a SegmentReader from the readerPool.  The reader
673
 
     * must be returned by calling {@link #release(SegmentReader)}
674
 
     * @see #release(SegmentReader)
675
 
     * @param info
676
 
     * @param doOpenStores
677
 
     * @throws IOException
678
 
     */
679
 
    public synchronized SegmentReader get(SegmentInfo info, boolean doOpenStores) throws IOException {
680
 
      return get(info, doOpenStores, BufferedIndexInput.BUFFER_SIZE, config.getReaderTermsIndexDivisor());
681
 
    }
682
 
 
683
 
    /**
684
 
     * Obtain a SegmentReader from the readerPool.  The reader
685
 
     * must be returned by calling {@link #release(SegmentReader)}
686
 
     * 
687
 
     * @see #release(SegmentReader)
688
 
     * @param info
689
 
     * @param doOpenStores
690
 
     * @param readBufferSize
691
 
     * @param termsIndexDivisor
692
 
     * @throws IOException
693
 
     */
694
 
    public synchronized SegmentReader get(SegmentInfo info, boolean doOpenStores, int readBufferSize, int termsIndexDivisor) throws IOException {
695
 
 
696
 
      if (poolReaders) {
697
 
        readBufferSize = BufferedIndexInput.BUFFER_SIZE;
698
 
      }
699
 
 
700
 
      SegmentReader sr = readerMap.get(info);
701
 
      if (sr == null) {
702
 
        // TODO: we may want to avoid doing this while
703
 
        // synchronized
704
 
        // Returns a ref, which we xfer to readerMap:
705
 
        sr = SegmentReader.get(false, info.dir, info, readBufferSize, doOpenStores, termsIndexDivisor);
706
 
        sr.readerFinishedListeners = readerFinishedListeners;
707
 
 
708
 
        if (info.dir == directory) {
709
 
          // Only pool if reader is not external
710
 
          readerMap.put(info, sr);
711
 
        }
712
 
      } else {
713
 
        if (doOpenStores) {
714
 
          sr.openDocStores();
715
 
        }
716
 
        if (termsIndexDivisor != -1 && !sr.termsIndexLoaded()) {
717
 
          // If this reader was originally opened because we
718
 
          // needed to merge it, we didn't load the terms
719
 
          // index.  But now, if the caller wants the terms
720
 
          // index (eg because it's doing deletes, or an NRT
721
 
          // reader is being opened) we ask the reader to
722
 
          // load its terms index.
723
 
          sr.loadTermsIndex(termsIndexDivisor);
724
 
        }
725
 
      }
726
 
 
727
 
      // Return a ref to our caller
728
 
      if (info.dir == directory) {
729
 
        // Only incRef if we pooled (reader is not external)
730
 
        sr.incRef();
731
 
      }
732
 
      return sr;
733
 
    }
734
 
 
735
 
    // Returns a ref
736
 
    public synchronized SegmentReader getIfExists(SegmentInfo info) throws IOException {
737
 
      SegmentReader sr = readerMap.get(info);
738
 
      if (sr != null) {
739
 
        sr.incRef();
740
 
      }
741
 
      return sr;
742
 
    }
743
 
  }
744
 
  
745
 
  
746
 
  
747
 
  /**
748
 
   * Obtain the number of deleted docs for a pooled reader.
749
 
   * If the reader isn't being pooled, the segmentInfo's 
750
 
   * delCount is returned.
751
 
   */
752
 
  public int numDeletedDocs(SegmentInfo info) throws IOException {
753
 
    ensureOpen(false);
754
 
    SegmentReader reader = readerPool.getIfExists(info);
755
 
    try {
756
 
      if (reader != null) {
757
 
        return reader.numDeletedDocs();
758
 
      } else {
759
 
        return info.getDelCount();
760
 
      }
761
 
    } finally {
762
 
      if (reader != null) {
763
 
        readerPool.release(reader);
764
 
      }
765
 
    }
766
 
  }
767
 
  
768
 
  /**
769
 
   * Used internally to throw an {@link
770
 
   * AlreadyClosedException} if this IndexWriter has been
771
 
   * closed.
772
 
   * @throws AlreadyClosedException if this IndexWriter is closed
773
 
   */
774
 
  protected final void ensureOpen(boolean includePendingClose) throws AlreadyClosedException {
775
 
    if (closed || (includePendingClose && closing)) {
776
 
      throw new AlreadyClosedException("this IndexWriter is closed");
777
 
    }
778
 
  }
779
 
 
780
 
  protected final void ensureOpen() throws AlreadyClosedException {
781
 
    ensureOpen(true);
782
 
  }
783
 
 
784
 
  /**
785
 
   * Prints a message to the infoStream (if non-null),
786
 
   * prefixed with the identifying information for this
787
 
   * writer and the thread that's calling it.
788
 
   */
789
 
  public void message(String message) {
790
 
    if (infoStream != null)
791
 
      infoStream.println("IW " + messageID + " [" + new Date() + "; " + Thread.currentThread().getName() + "]: " + message);
792
 
  }
793
 
 
794
 
  /**
795
 
   * Casts current mergePolicy to LogMergePolicy, and throws
796
 
   * an exception if the mergePolicy is not a LogMergePolicy.
797
 
   */
798
 
  private LogMergePolicy getLogMergePolicy() {
799
 
    if (mergePolicy instanceof LogMergePolicy)
800
 
      return (LogMergePolicy) mergePolicy;
801
 
    else
802
 
      throw new IllegalArgumentException("this method can only be called when the merge policy is the default LogMergePolicy");
803
 
  }
804
 
 
805
 
  /** <p>Get the current setting of whether newly flushed
806
 
   *  segments will use the compound file format.  Note that
807
 
   *  this just returns the value previously set with
808
 
   *  setUseCompoundFile(boolean), or the default value
809
 
   *  (true).  You cannot use this to query the status of
810
 
   *  previously flushed segments.</p>
811
 
   *
812
 
   *  <p>Note that this method is a convenience method: it
813
 
   *  just calls mergePolicy.getUseCompoundFile as long as
814
 
   *  mergePolicy is an instance of {@link LogMergePolicy}.
815
 
   *  Otherwise an IllegalArgumentException is thrown.</p>
816
 
   *
817
 
   *  @see #setUseCompoundFile(boolean)
818
 
   *  @deprecated use {@link LogMergePolicy#getUseCompoundFile()}
819
 
   */
820
 
  @Deprecated
821
 
  public boolean getUseCompoundFile() {
822
 
    return getLogMergePolicy().getUseCompoundFile();
823
 
  }
824
 
 
825
 
  /**
826
 
   * <p>
827
 
   * Setting to turn on usage of a compound file. When on, multiple files for
828
 
   * each segment are merged into a single file when a new segment is flushed.
829
 
   * </p>
830
 
   * 
831
 
   * <p>
832
 
   * Note that this method is a convenience method: it just calls
833
 
   * mergePolicy.setUseCompoundFile as long as mergePolicy is an instance of
834
 
   * {@link LogMergePolicy}. Otherwise an IllegalArgumentException is thrown.
835
 
   * </p>
836
 
   * 
837
 
   * @deprecated use {@link LogMergePolicy#setUseCompoundFile(boolean)}.
838
 
   */
839
 
  @Deprecated
840
 
  public void setUseCompoundFile(boolean value) {
841
 
    getLogMergePolicy().setUseCompoundFile(value);
842
 
  }
843
 
 
844
 
  /** Expert: Set the Similarity implementation used by this IndexWriter.
845
 
   *
846
 
   * @see Similarity#setDefault(Similarity)
847
 
   * @deprecated use {@link IndexWriterConfig#setSimilarity(Similarity)} instead
848
 
   */
849
 
  @Deprecated
850
 
  public void setSimilarity(Similarity similarity) {
851
 
    ensureOpen();
852
 
    this.similarity = similarity;
853
 
    docWriter.setSimilarity(similarity);
854
 
    // Required so config.getSimilarity returns the right value. But this will
855
 
    // go away together with the method in 4.0.
856
 
    config.setSimilarity(similarity);
857
 
  }
858
 
 
859
 
  /** Expert: Return the Similarity implementation used by this IndexWriter.
860
 
   *
861
 
   * <p>This defaults to the current value of {@link Similarity#getDefault()}.
862
 
   * @deprecated use {@link IndexWriterConfig#getSimilarity()} instead
863
 
   */
864
 
  @Deprecated
865
 
  public Similarity getSimilarity() {
866
 
    ensureOpen();
867
 
    return similarity;
868
 
  }
869
 
 
870
 
  /** Expert: Set the interval between indexed terms.  Large values cause less
871
 
   * memory to be used by IndexReader, but slow random-access to terms.  Small
872
 
   * values cause more memory to be used by an IndexReader, and speed
873
 
   * random-access to terms.
874
 
   *
875
 
   * This parameter determines the amount of computation required per query
876
 
   * term, regardless of the number of documents that contain that term.  In
877
 
   * particular, it is the maximum number of other terms that must be
878
 
   * scanned before a term is located and its frequency and position information
879
 
   * may be processed.  In a large index with user-entered query terms, query
880
 
   * processing time is likely to be dominated not by term lookup but rather
881
 
   * by the processing of frequency and positional data.  In a small index
882
 
   * or when many uncommon query terms are generated (e.g., by wildcard
883
 
   * queries) term lookup may become a dominant cost.
884
 
   *
885
 
   * In particular, <code>numUniqueTerms/interval</code> terms are read into
886
 
   * memory by an IndexReader, and, on average, <code>interval/2</code> terms
887
 
   * must be scanned for each random term access.
888
 
   *
889
 
   * @see #DEFAULT_TERM_INDEX_INTERVAL
890
 
   * @deprecated use {@link IndexWriterConfig#setTermIndexInterval(int)}
891
 
   */
892
 
  @Deprecated
893
 
  public void setTermIndexInterval(int interval) {
894
 
    ensureOpen();
895
 
    config.setTermIndexInterval(interval);
896
 
  }
897
 
 
898
 
  /** Expert: Return the interval between indexed terms.
899
 
   *
900
 
   * @see #setTermIndexInterval(int)
901
 
   * @deprecated use {@link IndexWriterConfig#getTermIndexInterval()}
902
 
   */
903
 
  @Deprecated
904
 
  public int getTermIndexInterval() {
905
 
    // We pass false because this method is called by SegmentMerger while we are in the process of closing
906
 
    ensureOpen(false);
907
 
    return config.getTermIndexInterval();
908
 
  }
909
 
 
910
 
  /**
911
 
   * Constructs an IndexWriter for the index in <code>d</code>.
912
 
   * Text will be analyzed with <code>a</code>.  If <code>create</code>
913
 
   * is true, then a new, empty index will be created in
914
 
   * <code>d</code>, replacing the index already there, if any.
915
 
   *
916
 
   * @param d the index directory
917
 
   * @param a the analyzer to use
918
 
   * @param create <code>true</code> to create the index or overwrite
919
 
   *  the existing one; <code>false</code> to append to the existing
920
 
   *  index
921
 
   * @param mfl Maximum field length in number of terms/tokens: LIMITED, UNLIMITED, or user-specified
922
 
   *   via the MaxFieldLength constructor.
923
 
   * @throws CorruptIndexException if the index is corrupt
924
 
   * @throws LockObtainFailedException if another writer
925
 
   *  has this index open (<code>write.lock</code> could not
926
 
   *  be obtained)
927
 
   * @throws IOException if the directory cannot be read/written to, or
928
 
   *  if it does not exist and <code>create</code> is
929
 
   *  <code>false</code> or if there is any other low-level
930
 
   *  IO error
931
 
   *  @deprecated use {@link #IndexWriter(Directory, IndexWriterConfig)} instead
932
 
   */
933
 
  @Deprecated
934
 
  public IndexWriter(Directory d, Analyzer a, boolean create, MaxFieldLength mfl)
935
 
       throws CorruptIndexException, LockObtainFailedException, IOException {
936
 
    this(d, new IndexWriterConfig(Version.LUCENE_31, a).setOpenMode(
937
 
        create ? OpenMode.CREATE : OpenMode.APPEND));
938
 
    setMaxFieldLength(mfl.getLimit());
939
 
  }
940
 
 
941
 
  /**
942
 
   * Constructs an IndexWriter for the index in
943
 
   * <code>d</code>, first creating it if it does not
944
 
   * already exist.  Text will be analyzed with
945
 
   * <code>a</code>.
946
 
   *
947
 
   * @param d the index directory
948
 
   * @param a the analyzer to use
949
 
   * @param mfl Maximum field length in number of terms/tokens: LIMITED, UNLIMITED, or user-specified
950
 
   *   via the MaxFieldLength constructor.
951
 
   * @throws CorruptIndexException if the index is corrupt
952
 
   * @throws LockObtainFailedException if another writer
953
 
   *  has this index open (<code>write.lock</code> could not
954
 
   *  be obtained)
955
 
   * @throws IOException if the directory cannot be
956
 
   *  read/written to or if there is any other low-level
957
 
   *  IO error
958
 
   *  @deprecated use {@link #IndexWriter(Directory, IndexWriterConfig)} instead
959
 
   */
960
 
  @Deprecated
961
 
  public IndexWriter(Directory d, Analyzer a, MaxFieldLength mfl)
962
 
    throws CorruptIndexException, LockObtainFailedException, IOException {
963
 
    this(d, new IndexWriterConfig(Version.LUCENE_31, a));
964
 
    setMaxFieldLength(mfl.getLimit());
965
 
  }
966
 
 
967
 
  /**
968
 
   * Expert: constructs an IndexWriter with a custom {@link
969
 
   * IndexDeletionPolicy}, for the index in <code>d</code>,
970
 
   * first creating it if it does not already exist.  Text
971
 
   * will be analyzed with <code>a</code>.
972
 
   *
973
 
   * @param d the index directory
974
 
   * @param a the analyzer to use
975
 
   * @param deletionPolicy see <a href="#deletionPolicy">above</a>
976
 
   * @param mfl whether or not to limit field lengths
977
 
   * @throws CorruptIndexException if the index is corrupt
978
 
   * @throws LockObtainFailedException if another writer
979
 
   *  has this index open (<code>write.lock</code> could not
980
 
   *  be obtained)
981
 
   * @throws IOException if the directory cannot be
982
 
   *  read/written to or if there is any other low-level
983
 
   *  IO error
984
 
   *  @deprecated use {@link #IndexWriter(Directory, IndexWriterConfig)} instead
985
 
   */
986
 
  @Deprecated
987
 
  public IndexWriter(Directory d, Analyzer a, IndexDeletionPolicy deletionPolicy, MaxFieldLength mfl)
988
 
    throws CorruptIndexException, LockObtainFailedException, IOException {
989
 
    this(d, new IndexWriterConfig(Version.LUCENE_31, a).setIndexDeletionPolicy(deletionPolicy));
990
 
    setMaxFieldLength(mfl.getLimit());
991
 
  }
992
 
 
993
 
  /**
994
 
   * Expert: constructs an IndexWriter with a custom {@link
995
 
   * IndexDeletionPolicy}, for the index in <code>d</code>.
996
 
   * Text will be analyzed with <code>a</code>.  If
997
 
   * <code>create</code> is true, then a new, empty index
998
 
   * will be created in <code>d</code>, replacing the index
999
 
   * already there, if any.
1000
 
   *
1001
 
   * @param d the index directory
1002
 
   * @param a the analyzer to use
1003
 
   * @param create <code>true</code> to create the index or overwrite
1004
 
   *  the existing one; <code>false</code> to append to the existing
1005
 
   *  index
1006
 
   * @param deletionPolicy see <a href="#deletionPolicy">above</a>
1007
 
   * @param mfl {@link org.apache.lucene.index.IndexWriter.MaxFieldLength}, whether or not to limit field lengths.  Value is in number of terms/tokens
1008
 
   * @throws CorruptIndexException if the index is corrupt
1009
 
   * @throws LockObtainFailedException if another writer
1010
 
   *  has this index open (<code>write.lock</code> could not
1011
 
   *  be obtained)
1012
 
   * @throws IOException if the directory cannot be read/written to, or
1013
 
   *  if it does not exist and <code>create</code> is
1014
 
   *  <code>false</code> or if there is any other low-level
1015
 
   *  IO error
1016
 
   *  @deprecated use {@link #IndexWriter(Directory, IndexWriterConfig)} instead
1017
 
   */
1018
 
  @Deprecated
1019
 
  public IndexWriter(Directory d, Analyzer a, boolean create, IndexDeletionPolicy deletionPolicy, MaxFieldLength mfl)
1020
 
       throws CorruptIndexException, LockObtainFailedException, IOException {
1021
 
    this(d, new IndexWriterConfig(Version.LUCENE_31, a).setOpenMode(
1022
 
        create ? OpenMode.CREATE : OpenMode.APPEND).setIndexDeletionPolicy(deletionPolicy));
1023
 
    setMaxFieldLength(mfl.getLimit());
1024
 
  }
1025
 
  
1026
 
  /**
1027
 
   * Expert: constructs an IndexWriter on specific commit
1028
 
   * point, with a custom {@link IndexDeletionPolicy}, for
1029
 
   * the index in <code>d</code>.  Text will be analyzed
1030
 
   * with <code>a</code>.
1031
 
   *
1032
 
   * <p> This is only meaningful if you've used a {@link
1033
 
   * IndexDeletionPolicy} in that past that keeps more than
1034
 
   * just the last commit.
1035
 
   * 
1036
 
   * <p>This operation is similar to {@link #rollback()},
1037
 
   * except that method can only rollback what's been done
1038
 
   * with the current instance of IndexWriter since its last
1039
 
   * commit, whereas this method can rollback to an
1040
 
   * arbitrary commit point from the past, assuming the
1041
 
   * {@link IndexDeletionPolicy} has preserved past
1042
 
   * commits.
1043
 
   *
1044
 
   * @param d the index directory
1045
 
   * @param a the analyzer to use
1046
 
   * @param deletionPolicy see <a href="#deletionPolicy">above</a>
1047
 
   * @param mfl whether or not to limit field lengths, value is in number of terms/tokens.  See {@link org.apache.lucene.index.IndexWriter.MaxFieldLength}.
1048
 
   * @param commit which commit to open
1049
 
   * @throws CorruptIndexException if the index is corrupt
1050
 
   * @throws LockObtainFailedException if another writer
1051
 
   *  has this index open (<code>write.lock</code> could not
1052
 
   *  be obtained)
1053
 
   * @throws IOException if the directory cannot be read/written to, or
1054
 
   *  if it does not exist and <code>create</code> is
1055
 
   *  <code>false</code> or if there is any other low-level
1056
 
   *  IO error
1057
 
   *  @deprecated use {@link #IndexWriter(Directory, IndexWriterConfig)} instead
1058
 
   */
1059
 
  @Deprecated
1060
 
  public IndexWriter(Directory d, Analyzer a, IndexDeletionPolicy deletionPolicy, MaxFieldLength mfl, IndexCommit commit)
1061
 
       throws CorruptIndexException, LockObtainFailedException, IOException {
1062
 
    this(d, new IndexWriterConfig(Version.LUCENE_31, a)
1063
 
        .setOpenMode(OpenMode.APPEND).setIndexDeletionPolicy(deletionPolicy).setIndexCommit(commit));
1064
 
    setMaxFieldLength(mfl.getLimit());
1065
 
  }
1066
 
 
1067
 
  /**
1068
 
   * Constructs a new IndexWriter per the settings given in <code>conf</code>.
1069
 
   * Note that the passed in {@link IndexWriterConfig} is
1070
 
   * privately cloned; if you need to make subsequent "live"
1071
 
   * changes to the configuration use {@link #getConfig}.
1072
 
   * <p>
1073
 
   * 
1074
 
   * @param d
1075
 
   *          the index directory. The index is either created or appended
1076
 
   *          according <code>conf.getOpenMode()</code>.
1077
 
   * @param conf
1078
 
   *          the configuration settings according to which IndexWriter should
1079
 
   *          be initialized.
1080
 
   * @throws CorruptIndexException
1081
 
   *           if the index is corrupt
1082
 
   * @throws LockObtainFailedException
1083
 
   *           if another writer has this index open (<code>write.lock</code>
1084
 
   *           could not be obtained)
1085
 
   * @throws IOException
1086
 
   *           if the directory cannot be read/written to, or if it does not
1087
 
   *           exist and <code>conf.getOpenMode()</code> is
1088
 
   *           <code>OpenMode.APPEND</code> or if there is any other low-level
1089
 
   *           IO error
1090
 
   */
1091
 
  public IndexWriter(Directory d, IndexWriterConfig conf)
1092
 
      throws CorruptIndexException, LockObtainFailedException, IOException {
1093
 
    config = (IndexWriterConfig) conf.clone();
1094
 
    directory = d;
1095
 
    analyzer = conf.getAnalyzer();
1096
 
    infoStream = defaultInfoStream;
1097
 
    writeLockTimeout = conf.getWriteLockTimeout();
1098
 
    similarity = conf.getSimilarity();
1099
 
    mergePolicy = conf.getMergePolicy();
1100
 
    mergePolicy.setIndexWriter(this);
1101
 
    mergeScheduler = conf.getMergeScheduler();
1102
 
    bufferedDeletesStream = new BufferedDeletesStream(messageID);
1103
 
    bufferedDeletesStream.setInfoStream(infoStream);
1104
 
    poolReaders = conf.getReaderPooling();
1105
 
 
1106
 
    writeLock = directory.makeLock(WRITE_LOCK_NAME);
1107
 
 
1108
 
    if (!writeLock.obtain(writeLockTimeout)) // obtain write lock
1109
 
      throw new LockObtainFailedException("Index locked for write: " + writeLock);
1110
 
 
1111
 
    OpenMode mode = conf.getOpenMode();
1112
 
    boolean create;
1113
 
    if (mode == OpenMode.CREATE) {
1114
 
      create = true;
1115
 
    } else if (mode == OpenMode.APPEND) {
1116
 
      create = false;
1117
 
    } else {
1118
 
      // CREATE_OR_APPEND - create only if an index does not exist
1119
 
      create = !IndexReader.indexExists(directory);
1120
 
    }
1121
 
    
1122
 
    boolean success = false;
1123
 
 
1124
 
    // TODO: we should check whether this index is too old,
1125
 
    // and throw an IndexFormatTooOldExc up front, here,
1126
 
    // instead of later when merge, applyDeletes, getReader
1127
 
    // is attempted.  I think to do this we should store the
1128
 
    // oldest segment's version in segments_N.
1129
 
 
1130
 
    try {
1131
 
      if (create) {
1132
 
        // Try to read first.  This is to allow create
1133
 
        // against an index that's currently open for
1134
 
        // searching.  In this case we write the next
1135
 
        // segments_N file with no segments:
1136
 
        try {
1137
 
          segmentInfos.read(directory);
1138
 
          segmentInfos.clear();
1139
 
        } catch (IOException e) {
1140
 
          // Likely this means it's a fresh directory
1141
 
        }
1142
 
 
1143
 
        // Record that we have a change (zero out all
1144
 
        // segments) pending:
1145
 
        changeCount++;
1146
 
        segmentInfos.changed();
1147
 
      } else {
1148
 
        segmentInfos.read(directory);
1149
 
 
1150
 
        IndexCommit commit = conf.getIndexCommit();
1151
 
        if (commit != null) {
1152
 
          // Swap out all segments, but, keep metadata in
1153
 
          // SegmentInfos, like version & generation, to
1154
 
          // preserve write-once.  This is important if
1155
 
          // readers are open against the future commit
1156
 
          // points.
1157
 
          if (commit.getDirectory() != directory)
1158
 
            throw new IllegalArgumentException("IndexCommit's directory doesn't match my directory");
1159
 
          SegmentInfos oldInfos = new SegmentInfos();
1160
 
          oldInfos.read(directory, commit.getSegmentsFileName());
1161
 
          segmentInfos.replace(oldInfos);
1162
 
          changeCount++;
1163
 
          segmentInfos.changed();
1164
 
          if (infoStream != null)
1165
 
            message("init: loaded commit \"" + commit.getSegmentsFileName() + "\"");
1166
 
        }
1167
 
      }
1168
 
 
1169
 
      rollbackSegments = segmentInfos.createBackupSegmentInfos(true);
1170
 
 
1171
 
      docWriter = new DocumentsWriter(config, directory, this, getCurrentFieldInfos(), bufferedDeletesStream);
1172
 
      docWriter.setInfoStream(infoStream);
1173
 
      docWriter.setMaxFieldLength(maxFieldLength);
1174
 
 
1175
 
      // Default deleter (for backwards compatibility) is
1176
 
      // KeepOnlyLastCommitDeleter:
1177
 
      synchronized(this) {
1178
 
        deleter = new IndexFileDeleter(directory,
1179
 
                                       conf.getIndexDeletionPolicy(),
1180
 
                                       segmentInfos, infoStream,
1181
 
                                       this);
1182
 
      }
1183
 
 
1184
 
      if (deleter.startingCommitDeleted) {
1185
 
        // Deletion policy deleted the "head" commit point.
1186
 
        // We have to mark ourself as changed so that if we
1187
 
        // are closed w/o any further changes we write a new
1188
 
        // segments_N file.
1189
 
        changeCount++;
1190
 
        segmentInfos.changed();
1191
 
      }
1192
 
 
1193
 
      if (infoStream != null) {
1194
 
        messageState();
1195
 
      }
1196
 
 
1197
 
      success = true;
1198
 
 
1199
 
    } finally {
1200
 
      if (!success) {
1201
 
        if (infoStream != null) {
1202
 
          message("init: hit exception on init; releasing write lock");
1203
 
        }
1204
 
        try {
1205
 
          writeLock.release();
1206
 
        } catch (Throwable t) {
1207
 
          // don't mask the original exception
1208
 
        }
1209
 
        writeLock = null;
1210
 
      }
1211
 
    }
1212
 
  }
1213
 
 
1214
 
  private FieldInfos getFieldInfos(SegmentInfo info) throws IOException {
1215
 
    Directory cfsDir = null;
1216
 
    try {
1217
 
      if (info.getUseCompoundFile()) {
1218
 
        cfsDir = new CompoundFileReader(directory, IndexFileNames.segmentFileName(info.name, IndexFileNames.COMPOUND_FILE_EXTENSION));
1219
 
      } else {
1220
 
        cfsDir = directory;
1221
 
      }
1222
 
      return new FieldInfos(cfsDir, IndexFileNames.segmentFileName(info.name, IndexFileNames.FIELD_INFOS_EXTENSION));
1223
 
    } finally {
1224
 
      if (info.getUseCompoundFile() && cfsDir != null) {
1225
 
        cfsDir.close();
1226
 
      }
1227
 
    }
1228
 
  }
1229
 
 
1230
 
  private FieldInfos getCurrentFieldInfos() throws IOException {
1231
 
    final FieldInfos fieldInfos;
1232
 
    if (segmentInfos.size() > 0) {
1233
 
      if (segmentInfos.getFormat() > SegmentInfos.FORMAT_DIAGNOSTICS) {
1234
 
        // Pre-3.1 index.  In this case we sweep all
1235
 
        // segments, merging their FieldInfos:
1236
 
        fieldInfos = new FieldInfos();
1237
 
        for(SegmentInfo info : segmentInfos) {
1238
 
          final FieldInfos segFieldInfos = getFieldInfos(info);
1239
 
          final int fieldCount = segFieldInfos.size();
1240
 
          for(int fieldNumber=0;fieldNumber<fieldCount;fieldNumber++) {
1241
 
            fieldInfos.add(segFieldInfos.fieldInfo(fieldNumber));
1242
 
          }
1243
 
        }
1244
 
      } else {
1245
 
        // Already a 3.1 index; just seed the FieldInfos
1246
 
        // from the last segment
1247
 
        fieldInfos = getFieldInfos(segmentInfos.info(segmentInfos.size()-1));
1248
 
      }
1249
 
    } else {
1250
 
      fieldInfos = new FieldInfos();
1251
 
    }
1252
 
    return fieldInfos;
1253
 
  }
1254
 
 
1255
 
  /**
1256
 
   * Returns the private {@link IndexWriterConfig}, cloned
1257
 
   * from the {@link IndexWriterConfig} passed to
1258
 
   * {@link #IndexWriter(Directory, IndexWriterConfig)}.
1259
 
   * <p>
1260
 
   * <b>NOTE:</b> some settings may be changed on the
1261
 
   * returned {@link IndexWriterConfig}, and will take
1262
 
   * effect in the current IndexWriter instance.  See the
1263
 
   * javadocs for the specific setters in {@link
1264
 
   * IndexWriterConfig} for details.
1265
 
   */
1266
 
  public IndexWriterConfig getConfig() {
1267
 
    ensureOpen(false);
1268
 
    return config;
1269
 
  }
1270
 
  
1271
 
  /**
1272
 
   * Expert: set the merge policy used by this writer.
1273
 
   * 
1274
 
   * @deprecated use {@link IndexWriterConfig#setMergePolicy(MergePolicy)} instead.
1275
 
   */
1276
 
  @Deprecated
1277
 
  public void setMergePolicy(MergePolicy mp) {
1278
 
    ensureOpen();
1279
 
    if (mp == null)
1280
 
      throw new NullPointerException("MergePolicy must be non-null");
1281
 
 
1282
 
    if (mergePolicy != mp)
1283
 
      mergePolicy.close();
1284
 
    mergePolicy = mp;
1285
 
    mergePolicy.setIndexWriter(this);
1286
 
    pushMaxBufferedDocs();
1287
 
    if (infoStream != null)
1288
 
      message("setMergePolicy " + mp);
1289
 
    // Required so config.getMergePolicy returns the right value. But this will
1290
 
    // go away together with the method in 4.0.
1291
 
    config.setMergePolicy(mp);
1292
 
  }
1293
 
 
1294
 
  /**
1295
 
   * Expert: returns the current MergePolicy in use by this writer.
1296
 
   * @see #setMergePolicy
1297
 
   * 
1298
 
   * @deprecated use {@link IndexWriterConfig#getMergePolicy()} instead
1299
 
   */
1300
 
  @Deprecated
1301
 
  public MergePolicy getMergePolicy() {
1302
 
    ensureOpen();
1303
 
    return mergePolicy;
1304
 
  }
1305
 
 
1306
 
  /**
1307
 
   * Expert: set the merge scheduler used by this writer.
1308
 
   * @deprecated use {@link IndexWriterConfig#setMergeScheduler(MergeScheduler)} instead
1309
 
   */
1310
 
  @Deprecated
1311
 
  synchronized public void setMergeScheduler(MergeScheduler mergeScheduler) throws CorruptIndexException, IOException {
1312
 
    ensureOpen();
1313
 
    if (mergeScheduler == null)
1314
 
      throw new NullPointerException("MergeScheduler must be non-null");
1315
 
 
1316
 
    if (this.mergeScheduler != mergeScheduler) {
1317
 
      finishMerges(true);
1318
 
      this.mergeScheduler.close();
1319
 
    }
1320
 
    this.mergeScheduler = mergeScheduler;
1321
 
    if (infoStream != null)
1322
 
      message("setMergeScheduler " + mergeScheduler);
1323
 
    // Required so config.getMergeScheduler returns the right value. But this will
1324
 
    // go away together with the method in 4.0.
1325
 
    config.setMergeScheduler(mergeScheduler);
1326
 
  }
1327
 
 
1328
 
  /**
1329
 
   * Expert: returns the current MergeScheduler in use by this
1330
 
   * writer.
1331
 
   * @see #setMergeScheduler(MergeScheduler)
1332
 
   * @deprecated use {@link IndexWriterConfig#getMergeScheduler()} instead
1333
 
   */
1334
 
  @Deprecated
1335
 
  public MergeScheduler getMergeScheduler() {
1336
 
    ensureOpen();
1337
 
    return mergeScheduler;
1338
 
  }
1339
 
 
1340
 
  /** <p>Determines the largest segment (measured by
1341
 
   * document count) that may be merged with other segments.
1342
 
   * Small values (e.g., less than 10,000) are best for
1343
 
   * interactive indexing, as this limits the length of
1344
 
   * pauses while indexing to a few seconds.  Larger values
1345
 
   * are best for batched indexing and speedier
1346
 
   * searches.</p>
1347
 
   *
1348
 
   * <p>The default value is {@link Integer#MAX_VALUE}.</p>
1349
 
   *
1350
 
   * <p>Note that this method is a convenience method: it
1351
 
   * just calls mergePolicy.setMaxMergeDocs as long as
1352
 
   * mergePolicy is an instance of {@link LogMergePolicy}.
1353
 
   * Otherwise an IllegalArgumentException is thrown.</p>
1354
 
   *
1355
 
   * <p>The default merge policy ({@link
1356
 
   * LogByteSizeMergePolicy}) also allows you to set this
1357
 
   * limit by net size (in MB) of the segment, using {@link
1358
 
   * LogByteSizeMergePolicy#setMaxMergeMB}.</p>
1359
 
   * @deprecated use {@link LogMergePolicy#setMaxMergeDocs(int)} directly.
1360
 
   */
1361
 
  @Deprecated
1362
 
  public void setMaxMergeDocs(int maxMergeDocs) {
1363
 
    getLogMergePolicy().setMaxMergeDocs(maxMergeDocs);
1364
 
  }
1365
 
 
1366
 
  /**
1367
 
   * <p>Returns the largest segment (measured by document
1368
 
   * count) that may be merged with other segments.</p>
1369
 
   *
1370
 
   * <p>Note that this method is a convenience method: it
1371
 
   * just calls mergePolicy.getMaxMergeDocs as long as
1372
 
   * mergePolicy is an instance of {@link LogMergePolicy}.
1373
 
   * Otherwise an IllegalArgumentException is thrown.</p>
1374
 
   *
1375
 
   * @see #setMaxMergeDocs
1376
 
   * @deprecated use {@link LogMergePolicy#getMaxMergeDocs()} directly.
1377
 
   */
1378
 
  @Deprecated
1379
 
  public int getMaxMergeDocs() {
1380
 
    return getLogMergePolicy().getMaxMergeDocs();
1381
 
  }
1382
 
 
1383
 
  /**
1384
 
   * The maximum number of terms that will be indexed for a single field in a
1385
 
   * document. This limits the amount of memory required for indexing, so that
1386
 
   * collections with very large files will not crash the indexing process by
1387
 
   * running out of memory. This setting refers to the number of running terms,
1388
 
   * not to the number of different terms.
1389
 
   * <p/>
1390
 
   * <strong>Note:</strong> this silently truncates large documents, excluding
1391
 
   * from the index all terms that occur further in the document. If you know
1392
 
   * your source documents are large, be sure to set this value high enough to
1393
 
   * accomodate the expected size. If you set it to Integer.MAX_VALUE, then the
1394
 
   * only limit is your memory, but you should anticipate an OutOfMemoryError.
1395
 
   * <p/>
1396
 
   * By default, no more than {@link #DEFAULT_MAX_FIELD_LENGTH} terms will be
1397
 
   * indexed for a field.
1398
 
   * 
1399
 
   * @deprecated use {@link LimitTokenCountAnalyzer} instead. Note that the
1400
 
   *             behvaior slightly changed - the analyzer limits the number of
1401
 
   *             tokens per token stream created, while this setting limits the
1402
 
   *             total number of tokens to index. This only matters if you index
1403
 
   *             many multi-valued fields though.
1404
 
   */
1405
 
  @Deprecated
1406
 
  public void setMaxFieldLength(int maxFieldLength) {
1407
 
    ensureOpen();
1408
 
    this.maxFieldLength = maxFieldLength;
1409
 
    docWriter.setMaxFieldLength(maxFieldLength);
1410
 
    if (infoStream != null)
1411
 
      message("setMaxFieldLength " + maxFieldLength);
1412
 
  }
1413
 
 
1414
 
  /**
1415
 
   * Returns the maximum number of terms that will be
1416
 
   * indexed for a single field in a document.
1417
 
   * @see #setMaxFieldLength
1418
 
   * @deprecated use {@link LimitTokenCountAnalyzer} to limit number of tokens.
1419
 
   */
1420
 
  @Deprecated
1421
 
  public int getMaxFieldLength() {
1422
 
    ensureOpen();
1423
 
    return maxFieldLength;
1424
 
  }
1425
 
 
1426
 
  /**
1427
 
   * @deprecated use {@link
1428
 
   *  IndexWriterConfig#setReaderTermsIndexDivisor} instead.
1429
 
   */
1430
 
  @Deprecated
1431
 
  public void setReaderTermsIndexDivisor(int divisor) {
1432
 
    ensureOpen();
1433
 
    config.setReaderTermsIndexDivisor(divisor);
1434
 
    if (infoStream != null) {
1435
 
      message("setReaderTermsIndexDivisor " + divisor);
1436
 
    }
1437
 
  }
1438
 
 
1439
 
  /**
1440
 
   * @deprecated use {@link
1441
 
   *  IndexWriterConfig#getReaderTermsIndexDivisor} instead.
1442
 
   */
1443
 
  @Deprecated
1444
 
  public int getReaderTermsIndexDivisor() {
1445
 
    ensureOpen();
1446
 
    return config.getReaderTermsIndexDivisor();
1447
 
  }
1448
 
 
1449
 
  /** Determines the minimal number of documents required
1450
 
   * before the buffered in-memory documents are flushed as
1451
 
   * a new Segment.  Large values generally gives faster
1452
 
   * indexing.
1453
 
   *
1454
 
   * <p>When this is set, the writer will flush every
1455
 
   * maxBufferedDocs added documents.  Pass in {@link
1456
 
   * #DISABLE_AUTO_FLUSH} to prevent triggering a flush due
1457
 
   * to number of buffered documents.  Note that if flushing
1458
 
   * by RAM usage is also enabled, then the flush will be
1459
 
   * triggered by whichever comes first.</p>
1460
 
   *
1461
 
   * <p>Disabled by default (writer flushes by RAM usage).</p>
1462
 
   *
1463
 
   * @throws IllegalArgumentException if maxBufferedDocs is
1464
 
   * enabled but smaller than 2, or it disables maxBufferedDocs
1465
 
   * when ramBufferSize is already disabled
1466
 
   * @see #setRAMBufferSizeMB
1467
 
   * @deprecated use {@link IndexWriterConfig#setMaxBufferedDocs(int)} instead.
1468
 
   */
1469
 
  @Deprecated
1470
 
  public void setMaxBufferedDocs(int maxBufferedDocs) {
1471
 
    ensureOpen();
1472
 
    pushMaxBufferedDocs();
1473
 
    if (infoStream != null) {
1474
 
      message("setMaxBufferedDocs " + maxBufferedDocs);
1475
 
    }
1476
 
    // Required so config.getMaxBufferedDocs returns the right value. But this
1477
 
    // will go away together with the method in 4.0.
1478
 
    config.setMaxBufferedDocs(maxBufferedDocs);
1479
 
  }
1480
 
 
1481
 
  /**
1482
 
   * If we are flushing by doc count (not by RAM usage), and
1483
 
   * using LogDocMergePolicy then push maxBufferedDocs down
1484
 
   * as its minMergeDocs, to keep backwards compatibility.
1485
 
   */
1486
 
  private void pushMaxBufferedDocs() {
1487
 
    if (config.getMaxBufferedDocs() != DISABLE_AUTO_FLUSH) {
1488
 
      final MergePolicy mp = mergePolicy;
1489
 
      if (mp instanceof LogDocMergePolicy) {
1490
 
        LogDocMergePolicy lmp = (LogDocMergePolicy) mp;
1491
 
        final int maxBufferedDocs = config.getMaxBufferedDocs();
1492
 
        if (lmp.getMinMergeDocs() != maxBufferedDocs) {
1493
 
          if (infoStream != null)
1494
 
            message("now push maxBufferedDocs " + maxBufferedDocs + " to LogDocMergePolicy");
1495
 
          lmp.setMinMergeDocs(maxBufferedDocs);
1496
 
        }
1497
 
      }
1498
 
    }
1499
 
  }
1500
 
 
1501
 
  /**
1502
 
   * Returns the number of buffered added documents that will
1503
 
   * trigger a flush if enabled.
1504
 
   * @see #setMaxBufferedDocs
1505
 
   * @deprecated use {@link IndexWriterConfig#getMaxBufferedDocs()} instead.
1506
 
   */
1507
 
  @Deprecated
1508
 
  public int getMaxBufferedDocs() {
1509
 
    ensureOpen();
1510
 
    return config.getMaxBufferedDocs();
1511
 
  }
1512
 
 
1513
 
  /** Determines the amount of RAM that may be used for
1514
 
   * buffering added documents and deletions before they are
1515
 
   * flushed to the Directory.  Generally for faster
1516
 
   * indexing performance it's best to flush by RAM usage
1517
 
   * instead of document count and use as large a RAM buffer
1518
 
   * as you can.
1519
 
   *
1520
 
   * <p>When this is set, the writer will flush whenever
1521
 
   * buffered documents and deletions use this much RAM.
1522
 
   * Pass in {@link #DISABLE_AUTO_FLUSH} to prevent
1523
 
   * triggering a flush due to RAM usage.  Note that if
1524
 
   * flushing by document count is also enabled, then the
1525
 
   * flush will be triggered by whichever comes first.</p>
1526
 
   *
1527
 
   * <p> <b>NOTE</b>: the account of RAM usage for pending
1528
 
   * deletions is only approximate.  Specifically, if you
1529
 
   * delete by Query, Lucene currently has no way to measure
1530
 
   * the RAM usage if individual Queries so the accounting
1531
 
   * will under-estimate and you should compensate by either
1532
 
   * calling commit() periodically yourself, or by using
1533
 
   * {@link #setMaxBufferedDeleteTerms} to flush by count
1534
 
   * instead of RAM usage (each buffered delete Query counts
1535
 
   * as one).
1536
 
   *
1537
 
   * <p> <b>NOTE</b>: because IndexWriter uses
1538
 
   * <code>int</code>s when managing its internal storage,
1539
 
   * the absolute maximum value for this setting is somewhat
1540
 
   * less than 2048 MB.  The precise limit depends on
1541
 
   * various factors, such as how large your documents are,
1542
 
   * how many fields have norms, etc., so it's best to set
1543
 
   * this value comfortably under 2048.</p>
1544
 
   *
1545
 
   * <p> The default value is {@link #DEFAULT_RAM_BUFFER_SIZE_MB}.</p>
1546
 
   * 
1547
 
   * @throws IllegalArgumentException if ramBufferSize is
1548
 
   * enabled but non-positive, or it disables ramBufferSize
1549
 
   * when maxBufferedDocs is already disabled
1550
 
   * @deprecated use {@link IndexWriterConfig#setRAMBufferSizeMB(double)} instead.
1551
 
   */
1552
 
  @Deprecated
1553
 
  public void setRAMBufferSizeMB(double mb) {
1554
 
    if (infoStream != null) {
1555
 
      message("setRAMBufferSizeMB " + mb);
1556
 
    }
1557
 
    // Required so config.getRAMBufferSizeMB returns the right value. But this
1558
 
    // will go away together with the method in 4.0.
1559
 
    config.setRAMBufferSizeMB(mb);
1560
 
  }
1561
 
 
1562
 
  /**
1563
 
   * Returns the value set by {@link #setRAMBufferSizeMB} if enabled.
1564
 
   * @deprecated use {@link IndexWriterConfig#getRAMBufferSizeMB()} instead.
1565
 
   */
1566
 
  @Deprecated
1567
 
  public double getRAMBufferSizeMB() {
1568
 
    return config.getRAMBufferSizeMB();
1569
 
  }
1570
 
 
1571
 
  /**
1572
 
   * <p>Determines the minimal number of delete terms required before the buffered
1573
 
   * in-memory delete terms are applied and flushed. If there are documents
1574
 
   * buffered in memory at the time, they are merged and a new segment is
1575
 
   * created.</p>
1576
 
 
1577
 
   * <p>Disabled by default (writer flushes by RAM usage).</p>
1578
 
   * 
1579
 
   * @throws IllegalArgumentException if maxBufferedDeleteTerms
1580
 
   * is enabled but smaller than 1
1581
 
   * @see #setRAMBufferSizeMB
1582
 
   * @deprecated use {@link IndexWriterConfig#setMaxBufferedDeleteTerms(int)} instead.
1583
 
   */
1584
 
  @Deprecated
1585
 
  public void setMaxBufferedDeleteTerms(int maxBufferedDeleteTerms) {
1586
 
    ensureOpen();
1587
 
    if (infoStream != null)
1588
 
      message("setMaxBufferedDeleteTerms " + maxBufferedDeleteTerms);
1589
 
    // Required so config.getMaxBufferedDeleteTerms returns the right value. But
1590
 
    // this will go away together with the method in 4.0.
1591
 
    config.setMaxBufferedDeleteTerms(maxBufferedDeleteTerms);
1592
 
  }
1593
 
 
1594
 
  /**
1595
 
   * Returns the number of buffered deleted terms that will
1596
 
   * trigger a flush if enabled.
1597
 
   * @see #setMaxBufferedDeleteTerms
1598
 
   * @deprecated use {@link IndexWriterConfig#getMaxBufferedDeleteTerms()} instead
1599
 
   */
1600
 
  @Deprecated
1601
 
  public int getMaxBufferedDeleteTerms() {
1602
 
    ensureOpen();
1603
 
    return config.getMaxBufferedDeleteTerms();
1604
 
  }
1605
 
 
1606
 
  /** Determines how often segment indices are merged by addDocument().  With
1607
 
   * smaller values, less RAM is used while indexing, and searches on
1608
 
   * unoptimized indices are faster, but indexing speed is slower.  With larger
1609
 
   * values, more RAM is used during indexing, and while searches on unoptimized
1610
 
   * indices are slower, indexing is faster.  Thus larger values (> 10) are best
1611
 
   * for batch index creation, and smaller values (< 10) for indices that are
1612
 
   * interactively maintained.
1613
 
   *
1614
 
   * <p>Note that this method is a convenience method: it
1615
 
   * just calls mergePolicy.setMergeFactor as long as
1616
 
   * mergePolicy is an instance of {@link LogMergePolicy}.
1617
 
   * Otherwise an IllegalArgumentException is thrown.</p>
1618
 
   *
1619
 
   * <p>This must never be less than 2.  The default value is 10.
1620
 
   * @deprecated use {@link LogMergePolicy#setMergeFactor(int)} directly.
1621
 
   */
1622
 
  @Deprecated
1623
 
  public void setMergeFactor(int mergeFactor) {
1624
 
    getLogMergePolicy().setMergeFactor(mergeFactor);
1625
 
  }
1626
 
 
1627
 
  /**
1628
 
   * <p>Returns the number of segments that are merged at
1629
 
   * once and also controls the total number of segments
1630
 
   * allowed to accumulate in the index.</p>
1631
 
   *
1632
 
   * <p>Note that this method is a convenience method: it
1633
 
   * just calls mergePolicy.getMergeFactor as long as
1634
 
   * mergePolicy is an instance of {@link LogMergePolicy}.
1635
 
   * Otherwise an IllegalArgumentException is thrown.</p>
1636
 
   *
1637
 
   * @see #setMergeFactor
1638
 
   * @deprecated use {@link LogMergePolicy#getMergeFactor()} directly.
1639
 
   */
1640
 
  @Deprecated
1641
 
  public int getMergeFactor() {
1642
 
    return getLogMergePolicy().getMergeFactor();
1643
 
  }
1644
 
 
1645
 
  /** If non-null, this will be the default infoStream used
1646
 
   * by a newly instantiated IndexWriter.
1647
 
   * @see #setInfoStream
1648
 
   */
1649
 
  public static void setDefaultInfoStream(PrintStream infoStream) {
1650
 
    IndexWriter.defaultInfoStream = infoStream;
1651
 
  }
1652
 
 
1653
 
  /**
1654
 
   * Returns the current default infoStream for newly
1655
 
   * instantiated IndexWriters.
1656
 
   * @see #setDefaultInfoStream
1657
 
   */
1658
 
  public static PrintStream getDefaultInfoStream() {
1659
 
    return IndexWriter.defaultInfoStream;
1660
 
  }
1661
 
 
1662
 
  /** If non-null, information about merges, deletes and a
1663
 
   * message when maxFieldLength is reached will be printed
1664
 
   * to this.
1665
 
   */
1666
 
  public void setInfoStream(PrintStream infoStream) throws IOException {
1667
 
    ensureOpen();
1668
 
    this.infoStream = infoStream;
1669
 
    docWriter.setInfoStream(infoStream);
1670
 
    deleter.setInfoStream(infoStream);
1671
 
    bufferedDeletesStream.setInfoStream(infoStream);
1672
 
    if (infoStream != null)
1673
 
      messageState();
1674
 
  }
1675
 
 
1676
 
  private void messageState() throws IOException {
1677
 
    message("\ndir=" + directory + "\n" +
1678
 
            "index=" + segString() + "\n" +
1679
 
            "version=" + Constants.LUCENE_VERSION + "\n" +
1680
 
            config.toString());
1681
 
  }
1682
 
 
1683
 
  /**
1684
 
   * Returns the current infoStream in use by this writer.
1685
 
   * @see #setInfoStream
1686
 
   */
1687
 
  public PrintStream getInfoStream() {
1688
 
    ensureOpen();
1689
 
    return infoStream;
1690
 
  }
1691
 
 
1692
 
  /** Returns true if verbosing is enabled (i.e., infoStream != null). */
1693
 
  public boolean verbose() {
1694
 
    return infoStream != null;
1695
 
  }
1696
 
  
1697
 
  /**
1698
 
   * Sets the maximum time to wait for a write lock (in milliseconds) for this instance of IndexWriter.  @see
1699
 
   * @see #setDefaultWriteLockTimeout to change the default value for all instances of IndexWriter.
1700
 
   * @deprecated use {@link IndexWriterConfig#setWriteLockTimeout(long)} instead
1701
 
   */
1702
 
  @Deprecated
1703
 
  public void setWriteLockTimeout(long writeLockTimeout) {
1704
 
    ensureOpen();
1705
 
    this.writeLockTimeout = writeLockTimeout;
1706
 
    // Required so config.getWriteLockTimeout returns the right value. But this
1707
 
    // will go away together with the method in 4.0.
1708
 
    config.setWriteLockTimeout(writeLockTimeout);
1709
 
  }
1710
 
 
1711
 
  /**
1712
 
   * Returns allowed timeout when acquiring the write lock.
1713
 
   * @see #setWriteLockTimeout
1714
 
   * @deprecated use {@link IndexWriterConfig#getWriteLockTimeout()}
1715
 
   */
1716
 
  @Deprecated
1717
 
  public long getWriteLockTimeout() {
1718
 
    ensureOpen();
1719
 
    return writeLockTimeout;
1720
 
  }
1721
 
 
1722
 
  /**
1723
 
   * Sets the default (for any instance of IndexWriter) maximum time to wait for a write lock (in
1724
 
   * milliseconds).
1725
 
   * @deprecated use {@link IndexWriterConfig#setDefaultWriteLockTimeout(long)} instead
1726
 
   */
1727
 
  @Deprecated
1728
 
  public static void setDefaultWriteLockTimeout(long writeLockTimeout) {
1729
 
    IndexWriterConfig.setDefaultWriteLockTimeout(writeLockTimeout);
1730
 
  }
1731
 
 
1732
 
  /**
1733
 
   * Returns default write lock timeout for newly
1734
 
   * instantiated IndexWriters.
1735
 
   * @see #setDefaultWriteLockTimeout
1736
 
   * @deprecated use {@link IndexWriterConfig#getDefaultWriteLockTimeout()} instead
1737
 
   */
1738
 
  @Deprecated
1739
 
  public static long getDefaultWriteLockTimeout() {
1740
 
    return IndexWriterConfig.getDefaultWriteLockTimeout();
1741
 
  }
1742
 
 
1743
 
  /**
1744
 
   * Commits all changes to an index and closes all
1745
 
   * associated files.  Note that this may be a costly
1746
 
   * operation, so, try to re-use a single writer instead of
1747
 
   * closing and opening a new one.  See {@link #commit()} for
1748
 
   * caveats about write caching done by some IO devices.
1749
 
   *
1750
 
   * <p> If an Exception is hit during close, eg due to disk
1751
 
   * full or some other reason, then both the on-disk index
1752
 
   * and the internal state of the IndexWriter instance will
1753
 
   * be consistent.  However, the close will not be complete
1754
 
   * even though part of it (flushing buffered documents)
1755
 
   * may have succeeded, so the write lock will still be
1756
 
   * held.</p>
1757
 
   * 
1758
 
   * <p> If you can correct the underlying cause (eg free up
1759
 
   * some disk space) then you can call close() again.
1760
 
   * Failing that, if you want to force the write lock to be
1761
 
   * released (dangerous, because you may then lose buffered
1762
 
   * docs in the IndexWriter instance) then you can do
1763
 
   * something like this:</p>
1764
 
   *
1765
 
   * <pre>
1766
 
   * try {
1767
 
   *   writer.close();
1768
 
   * } finally {
1769
 
   *   if (IndexWriter.isLocked(directory)) {
1770
 
   *     IndexWriter.unlock(directory);
1771
 
   *   }
1772
 
   * }
1773
 
   * </pre>
1774
 
   *
1775
 
   * after which, you must be certain not to use the writer
1776
 
   * instance anymore.</p>
1777
 
   *
1778
 
   * <p><b>NOTE</b>: if this method hits an OutOfMemoryError
1779
 
   * you should immediately close the writer, again.  See <a
1780
 
   * href="#OOME">above</a> for details.</p>
1781
 
   *
1782
 
   * @throws CorruptIndexException if the index is corrupt
1783
 
   * @throws IOException if there is a low-level IO error
1784
 
   */
1785
 
  public void close() throws CorruptIndexException, IOException {
1786
 
    close(true);
1787
 
  }
1788
 
 
1789
 
  /**
1790
 
   * Closes the index with or without waiting for currently
1791
 
   * running merges to finish.  This is only meaningful when
1792
 
   * using a MergeScheduler that runs merges in background
1793
 
   * threads.
1794
 
   *
1795
 
   * <p><b>NOTE</b>: if this method hits an OutOfMemoryError
1796
 
   * you should immediately close the writer, again.  See <a
1797
 
   * href="#OOME">above</a> for details.</p>
1798
 
   *
1799
 
   * <p><b>NOTE</b>: it is dangerous to always call
1800
 
   * close(false), especially when IndexWriter is not open
1801
 
   * for very long, because this can result in "merge
1802
 
   * starvation" whereby long merges will never have a
1803
 
   * chance to finish.  This will cause too many segments in
1804
 
   * your index over time.</p>
1805
 
   *
1806
 
   * @param waitForMerges if true, this call will block
1807
 
   * until all merges complete; else, it will ask all
1808
 
   * running merges to abort, wait until those merges have
1809
 
   * finished (which should be at most a few seconds), and
1810
 
   * then return.
1811
 
   */
1812
 
  public void close(boolean waitForMerges) throws CorruptIndexException, IOException {
1813
 
 
1814
 
    // Ensure that only one thread actually gets to do the closing:
1815
 
    if (shouldClose()) {
1816
 
      // If any methods have hit OutOfMemoryError, then abort
1817
 
      // on close, in case the internal state of IndexWriter
1818
 
      // or DocumentsWriter is corrupt
1819
 
      if (hitOOM)
1820
 
        rollbackInternal();
1821
 
      else
1822
 
        closeInternal(waitForMerges);
1823
 
    }
1824
 
  }
1825
 
 
1826
 
  // Returns true if this thread should attempt to close, or
1827
 
  // false if IndexWriter is now closed; else, waits until
1828
 
  // another thread finishes closing
1829
 
  synchronized private boolean shouldClose() {
1830
 
    while(true) {
1831
 
      if (!closed) {
1832
 
        if (!closing) {
1833
 
          closing = true;
1834
 
          return true;
1835
 
        } else {
1836
 
          // Another thread is presently trying to close;
1837
 
          // wait until it finishes one way (closes
1838
 
          // successfully) or another (fails to close)
1839
 
          doWait();
1840
 
        }
1841
 
      } else
1842
 
        return false;
1843
 
    }
1844
 
  }
1845
 
 
1846
 
  private void closeInternal(boolean waitForMerges) throws CorruptIndexException, IOException {
1847
 
 
1848
 
    try {
1849
 
      if (infoStream != null) {
1850
 
        message("now flush at close waitForMerges=" + waitForMerges);
1851
 
      }
1852
 
 
1853
 
      docWriter.close();
1854
 
 
1855
 
      // Only allow a new merge to be triggered if we are
1856
 
      // going to wait for merges:
1857
 
      if (!hitOOM) {
1858
 
        flush(waitForMerges, true);
1859
 
      }
1860
 
 
1861
 
      if (waitForMerges)
1862
 
        // Give merge scheduler last chance to run, in case
1863
 
        // any pending merges are waiting:
1864
 
        mergeScheduler.merge(this);
1865
 
 
1866
 
      mergePolicy.close();
1867
 
 
1868
 
      synchronized(this) {
1869
 
        finishMerges(waitForMerges);
1870
 
        stopMerges = true;
1871
 
      }
1872
 
 
1873
 
      mergeScheduler.close();
1874
 
 
1875
 
      if (infoStream != null)
1876
 
        message("now call final commit()");
1877
 
      
1878
 
      if (!hitOOM) {
1879
 
        commitInternal(null);
1880
 
      }
1881
 
 
1882
 
      if (infoStream != null)
1883
 
        message("at close: " + segString());
1884
 
 
1885
 
      synchronized(this) {
1886
 
        readerPool.close();
1887
 
        docWriter = null;
1888
 
        deleter.close();
1889
 
      }
1890
 
      
1891
 
      if (writeLock != null) {
1892
 
        writeLock.release();                          // release write lock
1893
 
        writeLock = null;
1894
 
      }
1895
 
      synchronized(this) {
1896
 
        closed = true;
1897
 
      }
1898
 
    } catch (OutOfMemoryError oom) {
1899
 
      handleOOM(oom, "closeInternal");
1900
 
    } finally {
1901
 
      synchronized(this) {
1902
 
        closing = false;
1903
 
        notifyAll();
1904
 
        if (!closed) {
1905
 
          if (infoStream != null)
1906
 
            message("hit exception while closing");
1907
 
        }
1908
 
      }
1909
 
    }
1910
 
  }
1911
 
 
1912
 
  /** Returns the Directory used by this index. */
1913
 
  public Directory getDirectory() {     
1914
 
    // Pass false because the flush during closing calls getDirectory
1915
 
    ensureOpen(false);
1916
 
    return directory;
1917
 
  }
1918
 
 
1919
 
  /** Returns the analyzer used by this index. */
1920
 
  public Analyzer getAnalyzer() {
1921
 
    ensureOpen();
1922
 
    return analyzer;
1923
 
  }
1924
 
 
1925
 
  /** Returns total number of docs in this index, including
1926
 
   *  docs not yet flushed (still in the RAM buffer),
1927
 
   *  not counting deletions.
1928
 
   *  @see #numDocs */
1929
 
  public synchronized int maxDoc() {
1930
 
    ensureOpen();
1931
 
    int count;
1932
 
    if (docWriter != null)
1933
 
      count = docWriter.getNumDocs();
1934
 
    else
1935
 
      count = 0;
1936
 
 
1937
 
    count += segmentInfos.totalDocCount();
1938
 
    return count;
1939
 
  }
1940
 
 
1941
 
  /** Returns total number of docs in this index, including
1942
 
   *  docs not yet flushed (still in the RAM buffer), and
1943
 
   *  including deletions.  <b>NOTE:</b> buffered deletions
1944
 
   *  are not counted.  If you really need these to be
1945
 
   *  counted you should call {@link #commit()} first.
1946
 
   *  @see #numDocs */
1947
 
  public synchronized int numDocs() throws IOException {
1948
 
    ensureOpen();
1949
 
    int count;
1950
 
    if (docWriter != null)
1951
 
      count = docWriter.getNumDocs();
1952
 
    else
1953
 
      count = 0;
1954
 
 
1955
 
    for (final SegmentInfo info : segmentInfos) {
1956
 
      count += info.docCount - numDeletedDocs(info);
1957
 
    }
1958
 
    return count;
1959
 
  }
1960
 
 
1961
 
  public synchronized boolean hasDeletions() throws IOException {
1962
 
    ensureOpen();
1963
 
    if (bufferedDeletesStream.any()) {
1964
 
      return true;
1965
 
    }
1966
 
    if (docWriter.anyDeletions()) {
1967
 
      return true;
1968
 
    }
1969
 
    for (final SegmentInfo info : segmentInfos) {
1970
 
      if (info.hasDeletions()) {
1971
 
        return true;
1972
 
      }
1973
 
    }
1974
 
    return false;
1975
 
  }
1976
 
 
1977
 
  /**
1978
 
   * The maximum number of terms that will be indexed for a single field in a
1979
 
   * document.  This limits the amount of memory required for indexing, so that
1980
 
   * collections with very large files will not crash the indexing process by
1981
 
   * running out of memory.<p/>
1982
 
   * Note that this effectively truncates large documents, excluding from the
1983
 
   * index terms that occur further in the document.  If you know your source
1984
 
   * documents are large, be sure to set this value high enough to accommodate
1985
 
   * the expected size.  If you set it to Integer.MAX_VALUE, then the only limit
1986
 
   * is your memory, but you should anticipate an OutOfMemoryError.<p/>
1987
 
   * By default, no more than 10,000 terms will be indexed for a field.
1988
 
   *
1989
 
   * @see MaxFieldLength
1990
 
   * @deprecated remove in 4.0
1991
 
   */
1992
 
  @Deprecated
1993
 
  private int maxFieldLength = DEFAULT_MAX_FIELD_LENGTH;
1994
 
 
1995
 
  /**
1996
 
   * Adds a document to this index.  If the document contains more than
1997
 
   * {@link #setMaxFieldLength(int)} terms for a given field, the remainder are
1998
 
   * discarded.
1999
 
   *
2000
 
   * <p> Note that if an Exception is hit (for example disk full)
2001
 
   * then the index will be consistent, but this document
2002
 
   * may not have been added.  Furthermore, it's possible
2003
 
   * the index will have one segment in non-compound format
2004
 
   * even when using compound files (when a merge has
2005
 
   * partially succeeded).</p>
2006
 
   *
2007
 
   * <p> This method periodically flushes pending documents
2008
 
   * to the Directory (see <a href="#flush">above</a>), and
2009
 
   * also periodically triggers segment merges in the index
2010
 
   * according to the {@link MergePolicy} in use.</p>
2011
 
   *
2012
 
   * <p>Merges temporarily consume space in the
2013
 
   * directory. The amount of space required is up to 1X the
2014
 
   * size of all segments being merged, when no
2015
 
   * readers/searchers are open against the index, and up to
2016
 
   * 2X the size of all segments being merged when
2017
 
   * readers/searchers are open against the index (see
2018
 
   * {@link #forceMerge(int)} for details). The sequence of
2019
 
   * primitive merge operations performed is governed by the
2020
 
   * merge policy.
2021
 
   *
2022
 
   * <p>Note that each term in the document can be no longer
2023
 
   * than 16383 characters, otherwise an
2024
 
   * IllegalArgumentException will be thrown.</p>
2025
 
   *
2026
 
   * <p>Note that it's possible to create an invalid Unicode
2027
 
   * string in java if a UTF16 surrogate pair is malformed.
2028
 
   * In this case, the invalid characters are silently
2029
 
   * replaced with the Unicode replacement character
2030
 
   * U+FFFD.</p>
2031
 
   *
2032
 
   * <p><b>NOTE</b>: if this method hits an OutOfMemoryError
2033
 
   * you should immediately close the writer.  See <a
2034
 
   * href="#OOME">above</a> for details.</p>
2035
 
   *
2036
 
   * @throws CorruptIndexException if the index is corrupt
2037
 
   * @throws IOException if there is a low-level IO error
2038
 
   */
2039
 
  public void addDocument(Document doc) throws CorruptIndexException, IOException {
2040
 
    addDocument(doc, analyzer);
2041
 
  }
2042
 
 
2043
 
  /**
2044
 
   * Adds a document to this index, using the provided analyzer instead of the
2045
 
   * value of {@link #getAnalyzer()}.  If the document contains more than
2046
 
   * {@link #setMaxFieldLength(int)} terms for a given field, the remainder are
2047
 
   * discarded.
2048
 
   *
2049
 
   * <p>See {@link #addDocument(Document)} for details on
2050
 
   * index and IndexWriter state after an Exception, and
2051
 
   * flushing/merging temporary free space requirements.</p>
2052
 
   *
2053
 
   * <p><b>NOTE</b>: if this method hits an OutOfMemoryError
2054
 
   * you should immediately close the writer.  See <a
2055
 
   * href="#OOME">above</a> for details.</p>
2056
 
   *
2057
 
   * @throws CorruptIndexException if the index is corrupt
2058
 
   * @throws IOException if there is a low-level IO error
2059
 
   */
2060
 
  public void addDocument(Document doc, Analyzer analyzer) throws CorruptIndexException, IOException {
2061
 
    ensureOpen();
2062
 
    boolean doFlush = false;
2063
 
    boolean success = false;
2064
 
    try {
2065
 
      try {
2066
 
        doFlush = docWriter.updateDocument(doc, analyzer, null);
2067
 
        success = true;
2068
 
      } finally {
2069
 
        if (!success && infoStream != null)
2070
 
          message("hit exception adding document");
2071
 
      }
2072
 
      if (doFlush)
2073
 
        flush(true, false);
2074
 
    } catch (OutOfMemoryError oom) {
2075
 
      handleOOM(oom, "addDocument");
2076
 
    }
2077
 
  }
2078
 
 
2079
 
  /**
2080
 
   * Atomically adds a block of documents with sequentially
2081
 
   * assigned document IDs, such that an external reader
2082
 
   * will see all or none of the documents.
2083
 
   *
2084
 
   * <p><b>WARNING</b>: the index does not currently record
2085
 
   * which documents were added as a block.  Today this is
2086
 
   * fine, because merging will preserve the block (as long
2087
 
   * as none them were deleted).  But it's possible in the
2088
 
   * future that Lucene may more aggressively re-order
2089
 
   * documents (for example, perhaps to obtain better index
2090
 
   * compression), in which case you may need to fully
2091
 
   * re-index your documents at that time.
2092
 
   *
2093
 
   * <p>See {@link #addDocument(Document)} for details on
2094
 
   * index and IndexWriter state after an Exception, and
2095
 
   * flushing/merging temporary free space requirements.</p>
2096
 
   *
2097
 
   * <p><b>NOTE</b>: tools that do offline splitting of an index
2098
 
   * (for example, IndexSplitter in contrib) or
2099
 
   * re-sorting of documents (for example, IndexSorter in
2100
 
   * contrib) are not aware of these atomically added documents
2101
 
   * and will likely break them up.  Use such tools at your
2102
 
   * own risk!
2103
 
   *
2104
 
   * <p><b>NOTE</b>: if this method hits an OutOfMemoryError
2105
 
   * you should immediately close the writer.  See <a
2106
 
   * href="#OOME">above</a> for details.</p>
2107
 
   *
2108
 
   * @throws CorruptIndexException if the index is corrupt
2109
 
   * @throws IOException if there is a low-level IO error
2110
 
   *
2111
 
   * @lucene.experimental
2112
 
   */
2113
 
  public void addDocuments(Collection<Document> docs) throws CorruptIndexException, IOException {
2114
 
    // TODO: if we backport DWPT we should change arg to Iterable<Document>
2115
 
    addDocuments(docs, analyzer);
2116
 
  }
2117
 
 
2118
 
  /**
2119
 
   * Atomically adds a block of documents, analyzed using the
2120
 
   * provided analyzer, with sequentially assigned document
2121
 
   * IDs, such that an external reader will see all or none
2122
 
   * of the documents. 
2123
 
   *
2124
 
   * @throws CorruptIndexException if the index is corrupt
2125
 
   * @throws IOException if there is a low-level IO error
2126
 
   *
2127
 
   * @lucene.experimental
2128
 
   */
2129
 
  public void addDocuments(Collection<Document> docs, Analyzer analyzer) throws CorruptIndexException, IOException {
2130
 
    // TODO: if we backport DWPT we should change arg to Iterable<Document>
2131
 
    updateDocuments(null, docs, analyzer);
2132
 
  }
2133
 
 
2134
 
  /**
2135
 
   * Atomically deletes documents matching the provided
2136
 
   * delTerm and adds a block of documents with sequentially
2137
 
   * assigned document IDs, such that an external reader
2138
 
   * will see all or none of the documents. 
2139
 
   *
2140
 
   * See {@link #addDocuments(Collection)}.
2141
 
   *
2142
 
   * @throws CorruptIndexException if the index is corrupt
2143
 
   * @throws IOException if there is a low-level IO error
2144
 
   *
2145
 
   * @lucene.experimental
2146
 
   */
2147
 
  public void updateDocuments(Term delTerm, Collection<Document> docs) throws CorruptIndexException, IOException {
2148
 
    // TODO: if we backport DWPT we should change arg to Iterable<Document>
2149
 
    updateDocuments(delTerm, docs, analyzer);
2150
 
  }
2151
 
 
2152
 
  /**
2153
 
   * Atomically deletes documents matching the provided
2154
 
   * delTerm and adds a block of documents, analyzed  using
2155
 
   * the provided analyzer, with sequentially
2156
 
   * assigned document IDs, such that an external reader
2157
 
   * will see all or none of the documents. 
2158
 
   *
2159
 
   * See {@link #addDocuments(Collection)}.
2160
 
   *
2161
 
   * @throws CorruptIndexException if the index is corrupt
2162
 
   * @throws IOException if there is a low-level IO error
2163
 
   *
2164
 
   * @lucene.experimental
2165
 
   */
2166
 
  public void updateDocuments(Term delTerm, Collection<Document> docs, Analyzer analyzer) throws CorruptIndexException, IOException {
2167
 
    // TODO: if we backport DWPT we should change arg to Iterable<Document>
2168
 
    ensureOpen();
2169
 
    try {
2170
 
      boolean success = false;
2171
 
      boolean doFlush = false;
2172
 
      try {
2173
 
        doFlush = docWriter.updateDocuments(docs, analyzer, delTerm);
2174
 
        success = true;
2175
 
      } finally {
2176
 
        if (!success && infoStream != null) {
2177
 
          message("hit exception updating document");
2178
 
        }
2179
 
      }
2180
 
      if (doFlush) {
2181
 
        flush(true, false);
2182
 
      }
2183
 
    } catch (OutOfMemoryError oom) {
2184
 
      handleOOM(oom, "updateDocuments");
2185
 
    }
2186
 
  }
2187
 
 
2188
 
  /**
2189
 
   * Deletes the document(s) containing <code>term</code>.
2190
 
   *
2191
 
   * <p><b>NOTE</b>: if this method hits an OutOfMemoryError
2192
 
   * you should immediately close the writer.  See <a
2193
 
   * href="#OOME">above</a> for details.</p>
2194
 
   *
2195
 
   * @param term the term to identify the documents to be deleted
2196
 
   * @throws CorruptIndexException if the index is corrupt
2197
 
   * @throws IOException if there is a low-level IO error
2198
 
   */
2199
 
  public void deleteDocuments(Term term) throws CorruptIndexException, IOException {
2200
 
    ensureOpen();
2201
 
    try {
2202
 
      if (docWriter.deleteTerm(term, false)) {
2203
 
        flush(true, false);
2204
 
      }
2205
 
    } catch (OutOfMemoryError oom) {
2206
 
      handleOOM(oom, "deleteDocuments(Term)");
2207
 
    }
2208
 
  }
2209
 
 
2210
 
  /**
2211
 
   * Deletes the document(s) containing any of the
2212
 
   * terms. All deletes are flushed at the same time.
2213
 
   *
2214
 
   * <p><b>NOTE</b>: if this method hits an OutOfMemoryError
2215
 
   * you should immediately close the writer.  See <a
2216
 
   * href="#OOME">above</a> for details.</p>
2217
 
   *
2218
 
   * @param terms array of terms to identify the documents
2219
 
   * to be deleted
2220
 
   * @throws CorruptIndexException if the index is corrupt
2221
 
   * @throws IOException if there is a low-level IO error
2222
 
   */
2223
 
  public void deleteDocuments(Term... terms) throws CorruptIndexException, IOException {
2224
 
    ensureOpen();
2225
 
    try {
2226
 
      if (docWriter.deleteTerms(terms)) {
2227
 
        flush(true, false);
2228
 
      }
2229
 
    } catch (OutOfMemoryError oom) {
2230
 
      handleOOM(oom, "deleteDocuments(Term..)");
2231
 
    }
2232
 
  }
2233
 
 
2234
 
  /**
2235
 
   * Deletes the document(s) matching the provided query.
2236
 
   *
2237
 
   * <p><b>NOTE</b>: if this method hits an OutOfMemoryError
2238
 
   * you should immediately close the writer.  See <a
2239
 
   * href="#OOME">above</a> for details.</p>
2240
 
   *
2241
 
   * @param query the query to identify the documents to be deleted
2242
 
   * @throws CorruptIndexException if the index is corrupt
2243
 
   * @throws IOException if there is a low-level IO error
2244
 
   */
2245
 
  public void deleteDocuments(Query query) throws CorruptIndexException, IOException {
2246
 
    ensureOpen();
2247
 
    try {
2248
 
      if (docWriter.deleteQuery(query)) {
2249
 
        flush(true, false);
2250
 
      }
2251
 
    } catch (OutOfMemoryError oom) {
2252
 
      handleOOM(oom, "deleteDocuments(Query)");
2253
 
    }
2254
 
  }
2255
 
 
2256
 
  /**
2257
 
   * Deletes the document(s) matching any of the provided queries.
2258
 
   * All deletes are flushed at the same time.
2259
 
   *
2260
 
   * <p><b>NOTE</b>: if this method hits an OutOfMemoryError
2261
 
   * you should immediately close the writer.  See <a
2262
 
   * href="#OOME">above</a> for details.</p>
2263
 
   *
2264
 
   * @param queries array of queries to identify the documents
2265
 
   * to be deleted
2266
 
   * @throws CorruptIndexException if the index is corrupt
2267
 
   * @throws IOException if there is a low-level IO error
2268
 
   */
2269
 
  public void deleteDocuments(Query... queries) throws CorruptIndexException, IOException {
2270
 
    ensureOpen();
2271
 
    try {
2272
 
      if (docWriter.deleteQueries(queries)) {
2273
 
        flush(true, false);
2274
 
      }
2275
 
    } catch (OutOfMemoryError oom) {
2276
 
      handleOOM(oom, "deleteDocuments(Query..)");
2277
 
    }
2278
 
  }
2279
 
 
2280
 
  /**
2281
 
   * Updates a document by first deleting the document(s)
2282
 
   * containing <code>term</code> and then adding the new
2283
 
   * document.  The delete and then add are atomic as seen
2284
 
   * by a reader on the same index (flush may happen only after
2285
 
   * the add).
2286
 
   *
2287
 
   * <p><b>NOTE</b>: if this method hits an OutOfMemoryError
2288
 
   * you should immediately close the writer.  See <a
2289
 
   * href="#OOME">above</a> for details.</p>
2290
 
   *
2291
 
   * @param term the term to identify the document(s) to be
2292
 
   * deleted
2293
 
   * @param doc the document to be added
2294
 
   * @throws CorruptIndexException if the index is corrupt
2295
 
   * @throws IOException if there is a low-level IO error
2296
 
   */
2297
 
  public void updateDocument(Term term, Document doc) throws CorruptIndexException, IOException {
2298
 
    ensureOpen();
2299
 
    updateDocument(term, doc, getAnalyzer());
2300
 
  }
2301
 
 
2302
 
  /**
2303
 
   * Updates a document by first deleting the document(s)
2304
 
   * containing <code>term</code> and then adding the new
2305
 
   * document.  The delete and then add are atomic as seen
2306
 
   * by a reader on the same index (flush may happen only after
2307
 
   * the add).
2308
 
   *
2309
 
   * <p><b>NOTE</b>: if this method hits an OutOfMemoryError
2310
 
   * you should immediately close the writer.  See <a
2311
 
   * href="#OOME">above</a> for details.</p>
2312
 
   *
2313
 
   * @param term the term to identify the document(s) to be
2314
 
   * deleted
2315
 
   * @param doc the document to be added
2316
 
   * @param analyzer the analyzer to use when analyzing the document
2317
 
   * @throws CorruptIndexException if the index is corrupt
2318
 
   * @throws IOException if there is a low-level IO error
2319
 
   */
2320
 
  public void updateDocument(Term term, Document doc, Analyzer analyzer)
2321
 
      throws CorruptIndexException, IOException {
2322
 
    ensureOpen();
2323
 
    try {
2324
 
      boolean doFlush = false;
2325
 
      boolean success = false;
2326
 
      try {
2327
 
        doFlush = docWriter.updateDocument(doc, analyzer, term);
2328
 
        success = true;
2329
 
      } finally {
2330
 
        if (!success && infoStream != null)
2331
 
          message("hit exception updating document");
2332
 
      }
2333
 
      if (doFlush) {
2334
 
        flush(true, false);
2335
 
      }
2336
 
    } catch (OutOfMemoryError oom) {
2337
 
      handleOOM(oom, "updateDocument");
2338
 
    }
2339
 
  }
2340
 
 
2341
 
  // for test purpose
2342
 
  final synchronized int getSegmentCount(){
2343
 
    return segmentInfos.size();
2344
 
  }
2345
 
 
2346
 
  // for test purpose
2347
 
  final synchronized int getNumBufferedDocuments(){
2348
 
    return docWriter.getNumDocs();
2349
 
  }
2350
 
 
2351
 
  // for test purpose
2352
 
  final synchronized int getDocCount(int i) {
2353
 
    if (i >= 0 && i < segmentInfos.size()) {
2354
 
      return segmentInfos.info(i).docCount;
2355
 
    } else {
2356
 
      return -1;
2357
 
    }
2358
 
  }
2359
 
 
2360
 
  // for test purpose
2361
 
  final int getFlushCount() {
2362
 
    return flushCount.get();
2363
 
  }
2364
 
 
2365
 
  // for test purpose
2366
 
  final int getFlushDeletesCount() {
2367
 
    return flushDeletesCount.get();
2368
 
  }
2369
 
 
2370
 
  final String newSegmentName() {
2371
 
    // Cannot synchronize on IndexWriter because that causes
2372
 
    // deadlock
2373
 
    synchronized(segmentInfos) {
2374
 
      // Important to increment changeCount so that the
2375
 
      // segmentInfos is written on close.  Otherwise we
2376
 
      // could close, re-open and re-return the same segment
2377
 
      // name that was previously returned which can cause
2378
 
      // problems at least with ConcurrentMergeScheduler.
2379
 
      changeCount++;
2380
 
      segmentInfos.changed();
2381
 
      return "_" + Integer.toString(segmentInfos.counter++, Character.MAX_RADIX);
2382
 
    }
2383
 
  }
2384
 
 
2385
 
  /** If non-null, information about merges will be printed to this.
2386
 
   */
2387
 
  private PrintStream infoStream;
2388
 
  private static PrintStream defaultInfoStream;
2389
 
 
2390
 
  /** This method has been deprecated, as it is horribly
2391
 
   *  inefficient and very rarely justified.  Lucene's
2392
 
   *  multi-segment search performance has improved over
2393
 
   *  time, and the default TieredMergePolicy now targets
2394
 
   *  segments with deletions.
2395
 
   *
2396
 
   * @deprecated */
2397
 
  @Deprecated
2398
 
  public void optimize() throws CorruptIndexException, IOException {
2399
 
    forceMerge(1, true);
2400
 
  }
2401
 
 
2402
 
  /** This method has been deprecated, as it is horribly
2403
 
   *  inefficient and very rarely justified.  Lucene's
2404
 
   *  multi-segment search performance has improved over
2405
 
   *  time, and the default TieredMergePolicy now targets
2406
 
   *  segments with deletions.
2407
 
   *
2408
 
   * @deprecated */
2409
 
  @Deprecated
2410
 
  public void optimize(int maxNumSegments) throws CorruptIndexException, IOException {
2411
 
    forceMerge(maxNumSegments, true);
2412
 
  }
2413
 
 
2414
 
  /** This method has been deprecated, as it is horribly
2415
 
   *  inefficient and very rarely justified.  Lucene's
2416
 
   *  multi-segment search performance has improved over
2417
 
   *  time, and the default TieredMergePolicy now targets
2418
 
   *  segments with deletions.
2419
 
   *
2420
 
   * @deprecated */
2421
 
  @Deprecated
2422
 
  public void optimize(boolean doWait) throws CorruptIndexException, IOException {
2423
 
    forceMerge(1, doWait);
2424
 
  }
2425
 
 
2426
 
  /**
2427
 
   * Forces merge policy to merge segments until there's <=
2428
 
   * maxNumSegments.  The actual merges to be
2429
 
   * executed are determined by the {@link MergePolicy}.
2430
 
   *
2431
 
   * <p>This is a horribly costly operation, especially when
2432
 
   * you pass a small {@code maxNumSegments}; usually you
2433
 
   * should only call this if the index is static (will no
2434
 
   * longer be changed).</p>
2435
 
   *
2436
 
   * <p>Note that this requires up to 2X the index size free
2437
 
   * space in your Directory (3X if you're using compound
2438
 
   * file format).  For example, if your index size is 10 MB
2439
 
   * then you need up to 20 MB free for this to complete (30
2440
 
   * MB if you're using compound file format).  Also,
2441
 
   * it's best to call {@link #commit()} afterwards,
2442
 
   * to allow IndexWriter to free up disk space.</p>
2443
 
   *
2444
 
   * <p>If some but not all readers re-open while merging
2445
 
   * is underway, this will cause > 2X temporary
2446
 
   * space to be consumed as those new readers will then
2447
 
   * hold open the temporary segments at that time.  It is
2448
 
   * best not to re-open readers while merging is running.</p>
2449
 
   *
2450
 
   * <p>The actual temporary usage could be much less than
2451
 
   * these figures (it depends on many factors).</p>
2452
 
   *
2453
 
   * <p>In general, once the this completes, the total size of the
2454
 
   * index will be less than the size of the starting index.
2455
 
   * It could be quite a bit smaller (if there were many
2456
 
   * pending deletes) or just slightly smaller.</p>
2457
 
   *
2458
 
   * <p>If an Exception is hit, for example
2459
 
   * due to disk full, the index will not be corrupt and no
2460
 
   * documents will have been lost.  However, it may have
2461
 
   * been partially merged (some segments were merged but
2462
 
   * not all), and it's possible that one of the segments in
2463
 
   * the index will be in non-compound format even when
2464
 
   * using compound file format.  This will occur when the
2465
 
   * Exception is hit during conversion of the segment into
2466
 
   * compound format.</p>
2467
 
   *
2468
 
   * <p>This call will merge those segments present in
2469
 
   * the index when the call started.  If other threads are
2470
 
   * still adding documents and flushing segments, those
2471
 
   * newly created segments will not be merged unless you
2472
 
   * call forceMerge again.</p>
2473
 
   *
2474
 
   * <p><b>NOTE</b>: if this method hits an OutOfMemoryError
2475
 
   * you should immediately close the writer.  See <a
2476
 
   * href="#OOME">above</a> for details.</p>
2477
 
   *
2478
 
   * <p><b>NOTE</b>: if you call {@link #close(boolean)}
2479
 
   * with <tt>false</tt>, which aborts all running merges,
2480
 
   * then any thread still running this method might hit a
2481
 
   * {@link MergePolicy.MergeAbortedException}.
2482
 
   *
2483
 
   * @throws CorruptIndexException if the index is corrupt
2484
 
   * @throws IOException if there is a low-level IO error
2485
 
   * @see MergePolicy#findMerges
2486
 
   *
2487
 
   * @param maxNumSegments maximum number of segments left
2488
 
   * in the index after merging finishes
2489
 
  */
2490
 
  public void forceMerge(int maxNumSegments) throws CorruptIndexException, IOException {
2491
 
    forceMerge(maxNumSegments, true);
2492
 
  }
2493
 
 
2494
 
  /** Just like {@link #forceMerge(int)}, except you can
2495
 
   *  specify whether the call should block until
2496
 
   *  all merging completes.  This is only meaningful with a
2497
 
   *  {@link MergeScheduler} that is able to run merges in
2498
 
   *  background threads.
2499
 
   *
2500
 
   *  <p><b>NOTE</b>: if this method hits an OutOfMemoryError
2501
 
   *  you should immediately close the writer.  See <a
2502
 
   *  href="#OOME">above</a> for details.</p>
2503
 
   */
2504
 
  public void forceMerge(int maxNumSegments, boolean doWait) throws CorruptIndexException, IOException {
2505
 
    ensureOpen();
2506
 
    
2507
 
    if (maxNumSegments < 1)
2508
 
      throw new IllegalArgumentException("maxNumSegments must be >= 1; got " + maxNumSegments);
2509
 
 
2510
 
    if (infoStream != null) {
2511
 
      message("forceMerge: index now " + segString());
2512
 
      message("now flush at forceMerge");
2513
 
    }
2514
 
 
2515
 
    flush(true, true);
2516
 
 
2517
 
    synchronized(this) {
2518
 
      resetMergeExceptions();
2519
 
      segmentsToMerge.clear();
2520
 
      for(SegmentInfo info : segmentInfos) {
2521
 
        segmentsToMerge.put(info, Boolean.TRUE);
2522
 
      }
2523
 
      mergeMaxNumSegments = maxNumSegments;
2524
 
      
2525
 
      // Now mark all pending & running merges as isMaxNumSegments:
2526
 
      for(final MergePolicy.OneMerge merge  : pendingMerges) {
2527
 
        merge.maxNumSegments = maxNumSegments;
2528
 
        segmentsToMerge.put(merge.info, Boolean.TRUE);
2529
 
      }
2530
 
 
2531
 
      for ( final MergePolicy.OneMerge merge: runningMerges ) {
2532
 
        merge.maxNumSegments = maxNumSegments;
2533
 
        segmentsToMerge.put(merge.info, Boolean.TRUE);
2534
 
      }
2535
 
    }
2536
 
 
2537
 
    maybeMerge(maxNumSegments);
2538
 
 
2539
 
    if (doWait) {
2540
 
      synchronized(this) {
2541
 
        while(true) {
2542
 
 
2543
 
          if (hitOOM) {
2544
 
            throw new IllegalStateException("this writer hit an OutOfMemoryError; cannot complete forceMerge");
2545
 
          }
2546
 
 
2547
 
          if (mergeExceptions.size() > 0) {
2548
 
            // Forward any exceptions in background merge
2549
 
            // threads to the current thread:
2550
 
            final int size = mergeExceptions.size();
2551
 
            for(int i=0;i<size;i++) {
2552
 
              final MergePolicy.OneMerge merge = mergeExceptions.get(i);
2553
 
              if (merge.maxNumSegments != -1) {
2554
 
                IOException err = new IOException("background merge hit exception: " + merge.segString(directory));
2555
 
                final Throwable t = merge.getException();
2556
 
                if (t != null)
2557
 
                  err.initCause(t);
2558
 
                throw err;
2559
 
              }
2560
 
            }
2561
 
          }
2562
 
 
2563
 
          if (maxNumSegmentsMergesPending())
2564
 
            doWait();
2565
 
          else
2566
 
            break;
2567
 
        }
2568
 
      }
2569
 
 
2570
 
      // If close is called while we are still
2571
 
      // running, throw an exception so the calling
2572
 
      // thread will know merging did not
2573
 
      // complete
2574
 
      ensureOpen();
2575
 
    }
2576
 
 
2577
 
    // NOTE: in the ConcurrentMergeScheduler case, when
2578
 
    // doWait is false, we can return immediately while
2579
 
    // background threads accomplish the merging
2580
 
  }
2581
 
 
2582
 
  /** Returns true if any merges in pendingMerges or
2583
 
   *  runningMerges are maxNumSegments merges. */
2584
 
  private synchronized boolean maxNumSegmentsMergesPending() {
2585
 
    for (final MergePolicy.OneMerge merge : pendingMerges) {
2586
 
      if (merge.maxNumSegments != -1)
2587
 
        return true;
2588
 
    }
2589
 
    
2590
 
    for (final MergePolicy.OneMerge merge : runningMerges) {
2591
 
      if (merge.maxNumSegments != -1)
2592
 
        return true;
2593
 
    }
2594
 
    
2595
 
    return false;
2596
 
  }
2597
 
 
2598
 
  /** This method has been deprecated, as it is horribly
2599
 
   *  inefficient and very rarely justified.  Lucene's
2600
 
   *  multi-segment search performance has improved over
2601
 
   *  time, and the default TieredMergePolicy now targets
2602
 
   *  segments with deletions.
2603
 
   *
2604
 
   * @deprecated */
2605
 
  @Deprecated
2606
 
  public void expungeDeletes(boolean doWait) throws CorruptIndexException, IOException {
2607
 
    forceMergeDeletes(doWait);
2608
 
  }
2609
 
 
2610
 
  /** Just like {@link #forceMergeDeletes()}, except you can
2611
 
   *  specify whether the call should block until the
2612
 
   *  operation completes.  This is only meaningful with a
2613
 
   *  {@link MergeScheduler} that is able to run merges in
2614
 
   *  background threads.
2615
 
   *
2616
 
   * <p><b>NOTE</b>: if this method hits an OutOfMemoryError
2617
 
   * you should immediately close the writer.  See <a
2618
 
   * href="#OOME">above</a> for details.</p>
2619
 
   *
2620
 
   * <p><b>NOTE</b>: if you call {@link #close(boolean)}
2621
 
   * with <tt>false</tt>, which aborts all running merges,
2622
 
   * then any thread still running this method might hit a
2623
 
   * {@link MergePolicy.MergeAbortedException}.
2624
 
   */
2625
 
  public void forceMergeDeletes(boolean doWait)
2626
 
    throws CorruptIndexException, IOException {
2627
 
    ensureOpen();
2628
 
 
2629
 
    flush(true, true);
2630
 
 
2631
 
    if (infoStream != null)
2632
 
      message("forceMergeDeletes: index now " + segString());
2633
 
 
2634
 
    MergePolicy.MergeSpecification spec;
2635
 
 
2636
 
    synchronized(this) {
2637
 
      spec = mergePolicy.findForcedDeletesMerges(segmentInfos);
2638
 
      if (spec != null) {
2639
 
        final int numMerges = spec.merges.size();
2640
 
        for(int i=0;i<numMerges;i++)
2641
 
          registerMerge(spec.merges.get(i));
2642
 
      }
2643
 
    }
2644
 
 
2645
 
    mergeScheduler.merge(this);
2646
 
 
2647
 
    if (spec != null && doWait) {
2648
 
      final int numMerges = spec.merges.size();
2649
 
      synchronized(this) {
2650
 
        boolean running = true;
2651
 
        while(running) {
2652
 
 
2653
 
          if (hitOOM) {
2654
 
            throw new IllegalStateException("this writer hit an OutOfMemoryError; cannot complete forceMergeDeletes");
2655
 
          }
2656
 
 
2657
 
          // Check each merge that MergePolicy asked us to
2658
 
          // do, to see if any of them are still running and
2659
 
          // if any of them have hit an exception.
2660
 
          running = false;
2661
 
          for(int i=0;i<numMerges;i++) {
2662
 
            final MergePolicy.OneMerge merge = spec.merges.get(i);
2663
 
            if (pendingMerges.contains(merge) || runningMerges.contains(merge))
2664
 
              running = true;
2665
 
            Throwable t = merge.getException();
2666
 
            if (t != null) {
2667
 
              IOException ioe = new IOException("background merge hit exception: " + merge.segString(directory));
2668
 
              ioe.initCause(t);
2669
 
              throw ioe;
2670
 
            }
2671
 
          }
2672
 
 
2673
 
          // If any of our merges are still running, wait:
2674
 
          if (running)
2675
 
            doWait();
2676
 
        }
2677
 
      }
2678
 
    }
2679
 
 
2680
 
    // NOTE: in the ConcurrentMergeScheduler case, when
2681
 
    // doWait is false, we can return immediately while
2682
 
    // background threads accomplish the merging
2683
 
  }
2684
 
 
2685
 
 
2686
 
  /** This method has been deprecated, as it is horribly
2687
 
   *  inefficient and very rarely justified.  Lucene's
2688
 
   *  multi-segment search performance has improved over
2689
 
   *  time, and the default TieredMergePolicy now targets
2690
 
   *  segments with deletions.
2691
 
   *
2692
 
   * @deprecated */
2693
 
  @Deprecated
2694
 
  public void expungeDeletes() throws CorruptIndexException, IOException {
2695
 
    forceMergeDeletes();
2696
 
  }
2697
 
 
2698
 
  /**
2699
 
   *  Forces merging of all segments that have deleted
2700
 
   *  documents.  The actual merges to be executed are
2701
 
   *  determined by the {@link MergePolicy}.  For example,
2702
 
   *  the default {@link TieredMergePolicy} will only
2703
 
   *  pick a segment if the percentage of
2704
 
   *  deleted docs is over 10%.
2705
 
   *
2706
 
   *  <p>This is often a horribly costly operation; rarely
2707
 
   *  is it warranted.</p>
2708
 
   *
2709
 
   *  <p>To see how
2710
 
   *  many deletions you have pending in your index, call
2711
 
   *  {@link IndexReader#numDeletedDocs}.</p>
2712
 
   *
2713
 
   *  <p><b>NOTE</b>: this method first flushes a new
2714
 
   *  segment (if there are indexed documents), and applies
2715
 
   *  all buffered deletes.
2716
 
   *
2717
 
   *  <p><b>NOTE</b>: if this method hits an OutOfMemoryError
2718
 
   *  you should immediately close the writer.  See <a
2719
 
   *  href="#OOME">above</a> for details.</p>
2720
 
   */
2721
 
  public void forceMergeDeletes() throws CorruptIndexException, IOException {
2722
 
    forceMergeDeletes(true);
2723
 
  }
2724
 
 
2725
 
  /**
2726
 
   * Expert: asks the mergePolicy whether any merges are
2727
 
   * necessary now and if so, runs the requested merges and
2728
 
   * then iterate (test again if merges are needed) until no
2729
 
   * more merges are returned by the mergePolicy.
2730
 
   *
2731
 
   * Explicit calls to maybeMerge() are usually not
2732
 
   * necessary. The most common case is when merge policy
2733
 
   * parameters have changed.
2734
 
   *
2735
 
   * <p><b>NOTE</b>: if this method hits an OutOfMemoryError
2736
 
   * you should immediately close the writer.  See <a
2737
 
   * href="#OOME">above</a> for details.</p>
2738
 
   */
2739
 
  public final void maybeMerge() throws CorruptIndexException, IOException {
2740
 
    maybeMerge(-1);
2741
 
  }
2742
 
 
2743
 
  private final void maybeMerge(int maxNumSegments) throws CorruptIndexException, IOException {
2744
 
    ensureOpen(false);
2745
 
    updatePendingMerges(maxNumSegments);
2746
 
    mergeScheduler.merge(this);
2747
 
  }
2748
 
 
2749
 
  private synchronized void updatePendingMerges(int maxNumSegments)
2750
 
    throws CorruptIndexException, IOException {
2751
 
    assert maxNumSegments == -1 || maxNumSegments > 0;
2752
 
 
2753
 
    if (stopMerges) {
2754
 
      return;
2755
 
    }
2756
 
 
2757
 
    // Do not start new merges if we've hit OOME
2758
 
    if (hitOOM) {
2759
 
      return;
2760
 
    }
2761
 
 
2762
 
    final MergePolicy.MergeSpecification spec;
2763
 
    if (maxNumSegments != -1) {
2764
 
      spec = mergePolicy.findForcedMerges(segmentInfos, maxNumSegments, Collections.unmodifiableMap(segmentsToMerge));
2765
 
      if (spec != null) {
2766
 
        final int numMerges = spec.merges.size();
2767
 
        for(int i=0;i<numMerges;i++) {
2768
 
          final MergePolicy.OneMerge merge = spec.merges.get(i);
2769
 
          merge.maxNumSegments = maxNumSegments;
2770
 
        }
2771
 
      }
2772
 
 
2773
 
    } else {
2774
 
      spec = mergePolicy.findMerges(segmentInfos);
2775
 
    }
2776
 
 
2777
 
    if (spec != null) {
2778
 
      final int numMerges = spec.merges.size();
2779
 
      for(int i=0;i<numMerges;i++) {
2780
 
        registerMerge(spec.merges.get(i));
2781
 
      }
2782
 
    }
2783
 
  }
2784
 
 
2785
 
  /** Expert: to be used by a {@link MergePolicy} to avoid
2786
 
   *  selecting merges for segments already being merged.
2787
 
   *  The returned collection is not cloned, and thus is
2788
 
   *  only safe to access if you hold IndexWriter's lock
2789
 
   *  (which you do when IndexWriter invokes the
2790
 
   *  MergePolicy).
2791
 
   *
2792
 
   *  <p>Do not alter the returned collection! */
2793
 
  public synchronized Collection<SegmentInfo> getMergingSegments() {
2794
 
    return mergingSegments;
2795
 
  }
2796
 
 
2797
 
  /** Expert: the {@link MergeScheduler} calls this method
2798
 
   *  to retrieve the next merge requested by the
2799
 
   *  MergePolicy
2800
 
   *
2801
 
   * @lucene.experimental
2802
 
   */
2803
 
  public synchronized MergePolicy.OneMerge getNextMerge() {
2804
 
    if (pendingMerges.size() == 0)
2805
 
      return null;
2806
 
    else {
2807
 
      // Advance the merge from pending to running
2808
 
      MergePolicy.OneMerge merge = pendingMerges.removeFirst();
2809
 
      runningMerges.add(merge);
2810
 
      return merge;
2811
 
    }
2812
 
  }
2813
 
 
2814
 
  /**
2815
 
   * Close the <code>IndexWriter</code> without committing
2816
 
   * any changes that have occurred since the last commit
2817
 
   * (or since it was opened, if commit hasn't been called).
2818
 
   * This removes any temporary files that had been created,
2819
 
   * after which the state of the index will be the same as
2820
 
   * it was when commit() was last called or when this
2821
 
   * writer was first opened.  This also clears a previous
2822
 
   * call to {@link #prepareCommit}.
2823
 
   * @throws IOException if there is a low-level IO error
2824
 
   */
2825
 
  public void rollback() throws IOException {
2826
 
    ensureOpen();
2827
 
 
2828
 
    // Ensure that only one thread actually gets to do the closing:
2829
 
    if (shouldClose())
2830
 
      rollbackInternal();
2831
 
  }
2832
 
 
2833
 
  private void rollbackInternal() throws IOException {
2834
 
 
2835
 
    boolean success = false;
2836
 
 
2837
 
    if (infoStream != null ) {
2838
 
      message("rollback");
2839
 
    }
2840
 
 
2841
 
    try {
2842
 
      synchronized(this) {
2843
 
        finishMerges(false);
2844
 
        stopMerges = true;
2845
 
      }
2846
 
 
2847
 
      if (infoStream != null ) {
2848
 
        message("rollback: done finish merges");
2849
 
      }
2850
 
 
2851
 
      // Must pre-close these two, in case they increment
2852
 
      // changeCount so that we can then set it to false
2853
 
      // before calling closeInternal
2854
 
      mergePolicy.close();
2855
 
      mergeScheduler.close();
2856
 
 
2857
 
      bufferedDeletesStream.clear();
2858
 
 
2859
 
      synchronized(this) {
2860
 
 
2861
 
        if (pendingCommit != null) {
2862
 
          pendingCommit.rollbackCommit(directory);
2863
 
          deleter.decRef(pendingCommit);
2864
 
          pendingCommit = null;
2865
 
          notifyAll();
2866
 
        }
2867
 
 
2868
 
        // Keep the same segmentInfos instance but replace all
2869
 
        // of its SegmentInfo instances.  This is so the next
2870
 
        // attempt to commit using this instance of IndexWriter
2871
 
        // will always write to a new generation ("write
2872
 
        // once").
2873
 
        segmentInfos.rollbackSegmentInfos(rollbackSegments);
2874
 
        if (infoStream != null ) {
2875
 
          message("rollback: infos=" + segString(segmentInfos));
2876
 
        }
2877
 
 
2878
 
        docWriter.abort();
2879
 
 
2880
 
        assert testPoint("rollback before checkpoint");
2881
 
 
2882
 
        // Ask deleter to locate unreferenced files & remove
2883
 
        // them:
2884
 
        deleter.checkpoint(segmentInfos, false);
2885
 
        deleter.refresh();
2886
 
      }
2887
 
 
2888
 
      // Don't bother saving any changes in our segmentInfos
2889
 
      readerPool.clear(null);
2890
 
 
2891
 
      lastCommitChangeCount = changeCount;
2892
 
 
2893
 
      success = true;
2894
 
    } catch (OutOfMemoryError oom) {
2895
 
      handleOOM(oom, "rollbackInternal");
2896
 
    } finally {
2897
 
      synchronized(this) {
2898
 
        if (!success) {
2899
 
          closing = false;
2900
 
          notifyAll();
2901
 
          if (infoStream != null)
2902
 
            message("hit exception during rollback");
2903
 
        }
2904
 
      }
2905
 
    }
2906
 
 
2907
 
    closeInternal(false);
2908
 
  }
2909
 
 
2910
 
  /**
2911
 
   * Delete all documents in the index.
2912
 
   *
2913
 
   * <p>This method will drop all buffered documents and will 
2914
 
   *    remove all segments from the index. This change will not be
2915
 
   *    visible until a {@link #commit()} has been called. This method
2916
 
   *    can be rolled back using {@link #rollback()}.</p>
2917
 
   *
2918
 
   * <p>NOTE: this method is much faster than using deleteDocuments( new MatchAllDocsQuery() ).</p>
2919
 
   *
2920
 
   * <p>NOTE: this method will forcefully abort all merges
2921
 
   *    in progress.  If other threads are running {@link
2922
 
   *    #forceMerge}, {@link #addIndexes(IndexReader[])} or
2923
 
   *    {@link #forceMergeDeletes} methods, they may receive
2924
 
   *    {@link MergePolicy.MergeAbortedException}s.
2925
 
   */
2926
 
  public synchronized void deleteAll() throws IOException {
2927
 
    ensureOpen();
2928
 
    try {
2929
 
 
2930
 
      // Abort any running merges
2931
 
      finishMerges(false);
2932
 
 
2933
 
      // Remove any buffered docs
2934
 
      docWriter.abort();
2935
 
 
2936
 
      // Remove all segments
2937
 
      segmentInfos.clear();
2938
 
 
2939
 
      // Ask deleter to locate unreferenced files & remove them:
2940
 
      deleter.checkpoint(segmentInfos, false);
2941
 
      deleter.refresh();
2942
 
 
2943
 
      // Don't bother saving any changes in our segmentInfos
2944
 
      readerPool.dropAll();
2945
 
 
2946
 
      // Mark that the index has changed
2947
 
      ++changeCount;
2948
 
      segmentInfos.changed();
2949
 
    } catch (OutOfMemoryError oom) {
2950
 
      handleOOM(oom, "deleteAll");
2951
 
    } finally {
2952
 
      if (infoStream != null) {
2953
 
        message("hit exception during deleteAll");
2954
 
      }
2955
 
    }
2956
 
  }
2957
 
 
2958
 
  private synchronized void finishMerges(boolean waitForMerges) throws IOException {
2959
 
    if (!waitForMerges) {
2960
 
 
2961
 
      stopMerges = true;
2962
 
 
2963
 
      // Abort all pending & running merges:
2964
 
      for (final MergePolicy.OneMerge merge : pendingMerges) {
2965
 
        if (infoStream != null)
2966
 
          message("now abort pending merge " + merge.segString(directory));
2967
 
        merge.abort();
2968
 
        mergeFinish(merge);
2969
 
      }
2970
 
      pendingMerges.clear();
2971
 
      
2972
 
      for (final MergePolicy.OneMerge merge : runningMerges) {
2973
 
        if (infoStream != null)
2974
 
          message("now abort running merge " + merge.segString(directory));
2975
 
        merge.abort();
2976
 
      }
2977
 
 
2978
 
      // These merges periodically check whether they have
2979
 
      // been aborted, and stop if so.  We wait here to make
2980
 
      // sure they all stop.  It should not take very long
2981
 
      // because the merge threads periodically check if
2982
 
      // they are aborted.
2983
 
      while(runningMerges.size() > 0) {
2984
 
        if (infoStream != null)
2985
 
          message("now wait for " + runningMerges.size() + " running merge to abort");
2986
 
        doWait();
2987
 
      }
2988
 
 
2989
 
      stopMerges = false;
2990
 
      notifyAll();
2991
 
 
2992
 
      assert 0 == mergingSegments.size();
2993
 
 
2994
 
      if (infoStream != null)
2995
 
        message("all running merges have aborted");
2996
 
 
2997
 
    } else {
2998
 
      // waitForMerges() will ensure any running addIndexes finishes.  
2999
 
      // It's fine if a new one attempts to start because from our
3000
 
      // caller above the call will see that we are in the
3001
 
      // process of closing, and will throw an
3002
 
      // AlreadyClosedException.
3003
 
      waitForMerges();
3004
 
    }
3005
 
  }
3006
 
 
3007
 
  /**
3008
 
   * Wait for any currently outstanding merges to finish.
3009
 
   *
3010
 
   * <p>It is guaranteed that any merges started prior to calling this method 
3011
 
   *    will have completed once this method completes.</p>
3012
 
   */
3013
 
  public synchronized void waitForMerges() {
3014
 
    ensureOpen(false);
3015
 
    if (infoStream != null) {
3016
 
      message("waitForMerges");
3017
 
    }
3018
 
    while(pendingMerges.size() > 0 || runningMerges.size() > 0) {
3019
 
      doWait();
3020
 
    }
3021
 
 
3022
 
    // sanity check
3023
 
    assert 0 == mergingSegments.size();
3024
 
 
3025
 
    if (infoStream != null) {
3026
 
      message("waitForMerges done");
3027
 
    }
3028
 
  }
3029
 
 
3030
 
  /**
3031
 
   * Called whenever the SegmentInfos has been updated and
3032
 
   * the index files referenced exist (correctly) in the
3033
 
   * index directory.
3034
 
   */
3035
 
  synchronized void checkpoint() throws IOException {
3036
 
    changeCount++;
3037
 
    segmentInfos.changed();
3038
 
    deleter.checkpoint(segmentInfos, false);
3039
 
  }
3040
 
 
3041
 
  private synchronized void resetMergeExceptions() {
3042
 
    mergeExceptions = new ArrayList<MergePolicy.OneMerge>();
3043
 
    mergeGen++;
3044
 
  }
3045
 
 
3046
 
  private void noDupDirs(Directory... dirs) {
3047
 
    HashSet<Directory> dups = new HashSet<Directory>();
3048
 
    for (Directory dir : dirs) {
3049
 
      if (dups.contains(dir))
3050
 
        throw new IllegalArgumentException("Directory " + dir + " appears more than once");
3051
 
      if (dir == directory)
3052
 
        throw new IllegalArgumentException("Cannot add directory to itself");
3053
 
      dups.add(dir);
3054
 
    }
3055
 
  }
3056
 
 
3057
 
  /**
3058
 
   * @deprecated use {@link #addIndexes(Directory...)} instead
3059
 
   */
3060
 
  @Deprecated
3061
 
  public void addIndexesNoOptimize(Directory... dirs)
3062
 
      throws CorruptIndexException, IOException {
3063
 
    addIndexes(dirs);
3064
 
  }
3065
 
 
3066
 
  /**
3067
 
   * Adds all segments from an array of indexes into this index.
3068
 
   *
3069
 
   * <p>This may be used to parallelize batch indexing. A large document
3070
 
   * collection can be broken into sub-collections. Each sub-collection can be
3071
 
   * indexed in parallel, on a different thread, process or machine. The
3072
 
   * complete index can then be created by merging sub-collection indexes
3073
 
   * with this method.
3074
 
   *
3075
 
   * <p>
3076
 
   * <b>NOTE:</b> the index in each {@link Directory} must not be
3077
 
   * changed (opened by a writer) while this method is
3078
 
   * running.  This method does not acquire a write lock in
3079
 
   * each input Directory, so it is up to the caller to
3080
 
   * enforce this.
3081
 
   *
3082
 
   * <p>This method is transactional in how Exceptions are
3083
 
   * handled: it does not commit a new segments_N file until
3084
 
   * all indexes are added.  This means if an Exception
3085
 
   * occurs (for example disk full), then either no indexes
3086
 
   * will have been added or they all will have been.
3087
 
   *
3088
 
   * <p>Note that this requires temporary free space in the
3089
 
   * {@link Directory} up to 2X the sum of all input indexes
3090
 
   * (including the starting index). If readers/searchers
3091
 
   * are open against the starting index, then temporary
3092
 
   * free space required will be higher by the size of the
3093
 
   * starting index (see {@link #forceMerge(int)} for details).
3094
 
   *
3095
 
   * <p>
3096
 
   * <b>NOTE:</b> this method only copies the segments of the incomning indexes
3097
 
   * and does not merge them. Therefore deleted documents are not removed and
3098
 
   * the new segments are not merged with the existing ones. Also, if the merge 
3099
 
   * policy allows compound files, then any segment that is not compound is 
3100
 
   * converted to such. However, if the segment is compound, it is copied as-is
3101
 
   * even if the merge policy does not allow compound files.
3102
 
   * 
3103
 
   * <p>
3104
 
   * <p>This requires this index not be among those to be added.
3105
 
   *
3106
 
   * <p>
3107
 
   * <b>NOTE</b>: if this method hits an OutOfMemoryError
3108
 
   * you should immediately close the writer. See <a
3109
 
   * href="#OOME">above</a> for details.
3110
 
   *
3111
 
   * @throws CorruptIndexException if the index is corrupt
3112
 
   * @throws IOException if there is a low-level IO error
3113
 
   */
3114
 
  public void addIndexes(Directory... dirs) throws CorruptIndexException, IOException {
3115
 
    ensureOpen();
3116
 
    
3117
 
    noDupDirs(dirs);
3118
 
 
3119
 
    try {
3120
 
      if (infoStream != null)
3121
 
        message("flush at addIndexes(Directory...)");
3122
 
      flush(false, true);
3123
 
      
3124
 
      int docCount = 0;
3125
 
      List<SegmentInfo> infos = new ArrayList<SegmentInfo>();
3126
 
      Comparator<String> versionComparator = StringHelper.getVersionComparator();
3127
 
      for (Directory dir : dirs) {
3128
 
        if (infoStream != null) {
3129
 
          message("addIndexes: process directory " + dir);
3130
 
        }
3131
 
        SegmentInfos sis = new SegmentInfos(); // read infos from dir
3132
 
        sis.read(dir);
3133
 
        final Set<String> dsFilesCopied = new HashSet<String>();
3134
 
        final Map<String, String> dsNames = new HashMap<String, String>();
3135
 
        for (SegmentInfo info : sis) {
3136
 
          assert !infos.contains(info): "dup info dir=" + info.dir + " name=" + info.name;
3137
 
          
3138
 
          docCount += info.docCount;
3139
 
          String newSegName = newSegmentName();
3140
 
          String dsName = info.getDocStoreSegment();
3141
 
          
3142
 
          if (infoStream != null) {
3143
 
            message("addIndexes: process segment origName=" + info.name + " newName=" + newSegName + " dsName=" + dsName + " info=" + info);
3144
 
          }
3145
 
          
3146
 
          // create CFS only if the source segment is not CFS, and MP agrees it
3147
 
          // should be CFS.
3148
 
          boolean createCFS;
3149
 
          synchronized (this) { // Guard segmentInfos
3150
 
            createCFS = !info.getUseCompoundFile()
3151
 
                && mergePolicy.useCompoundFile(segmentInfos, info)
3152
 
                // optimize case only for segments that don't share doc stores
3153
 
                && versionComparator.compare(info.getVersion(), "3.1") >= 0;
3154
 
          }
3155
 
 
3156
 
          if (createCFS) {
3157
 
            copySegmentIntoCFS(info, newSegName);
3158
 
          } else {
3159
 
            copySegmentAsIs(info, newSegName, dsNames, dsFilesCopied);
3160
 
          }
3161
 
          infos.add(info);
3162
 
        }
3163
 
      }      
3164
 
 
3165
 
      synchronized (this) {
3166
 
        ensureOpen();
3167
 
        segmentInfos.addAll(infos);
3168
 
        checkpoint();
3169
 
      }
3170
 
      
3171
 
    } catch (OutOfMemoryError oom) {
3172
 
      handleOOM(oom, "addIndexes(Directory...)");
3173
 
    }
3174
 
  }
3175
 
 
3176
 
  /** 
3177
 
   * Merges the provided indexes into this index. This method is useful 
3178
 
   * if you use extensions of {@link IndexReader}. Otherwise, using 
3179
 
   * {@link #addIndexes(Directory...)} is highly recommended for performance 
3180
 
   * reasons. It uses the {@link MergeScheduler} and {@link MergePolicy} set 
3181
 
   * on this writer, which may perform merges in parallel.
3182
 
   * 
3183
 
   * <p>The provided IndexReaders are not closed.
3184
 
   *
3185
 
   * <p><b>NOTE:</b> this method does not merge the current segments, 
3186
 
   * only the incoming ones.
3187
 
   * 
3188
 
   * <p>See {@link #addIndexes(Directory...)} for details on transactional 
3189
 
   * semantics, temporary free space required in the Directory, 
3190
 
   * and non-CFS segments on an Exception.
3191
 
   *
3192
 
   * <p><b>NOTE</b>: if this method hits an OutOfMemoryError
3193
 
   * you should immediately close the writer.  See <a
3194
 
   * href="#OOME">above</a> for details.
3195
 
   *
3196
 
   * <p><b>NOTE</b>: if you call {@link #close(boolean)}
3197
 
   * with <tt>false</tt>, which aborts all running merges,
3198
 
   * then any thread still running this method might hit a
3199
 
   * {@link MergePolicy.MergeAbortedException}.
3200
 
   *
3201
 
   * @throws CorruptIndexException if the index is corrupt
3202
 
   * @throws IOException if there is a low-level IO error
3203
 
   */
3204
 
  public void addIndexes(IndexReader... readers) throws CorruptIndexException, IOException {
3205
 
 
3206
 
    ensureOpen();
3207
 
 
3208
 
    try {
3209
 
      if (infoStream != null)
3210
 
        message("flush at addIndexes(IndexReader...)");
3211
 
      flush(false, true);
3212
 
 
3213
 
      String mergedName = newSegmentName();
3214
 
      // TODO: somehow we should fix this merge so it's
3215
 
      // abortable so that IW.close(false) is able to stop it
3216
 
      SegmentMerger merger = new SegmentMerger(directory, config.getTermIndexInterval(),
3217
 
                                               mergedName, null, payloadProcessorProvider,
3218
 
                                               ((FieldInfos) docWriter.getFieldInfos().clone()));
3219
 
      
3220
 
      for (IndexReader reader : readers)      // add new indexes
3221
 
        merger.add(reader);
3222
 
      
3223
 
      int docCount = merger.merge();                // merge 'em
3224
 
      
3225
 
      SegmentInfo info = new SegmentInfo(mergedName, docCount, directory,
3226
 
                                         false, true,
3227
 
                                         merger.fieldInfos().hasProx(),
3228
 
                                         merger.fieldInfos().hasVectors());
3229
 
      setDiagnostics(info, "addIndexes(IndexReader...)");
3230
 
 
3231
 
      boolean useCompoundFile;
3232
 
      synchronized(this) { // Guard segmentInfos
3233
 
        if (stopMerges) {
3234
 
          deleter.deleteNewFiles(info.files());
3235
 
          return;
3236
 
        }
3237
 
        ensureOpen();
3238
 
        useCompoundFile = mergePolicy.useCompoundFile(segmentInfos, info);
3239
 
      }
3240
 
 
3241
 
      // Now create the compound file if needed
3242
 
      if (useCompoundFile) {
3243
 
        merger.createCompoundFile(mergedName + ".cfs", info);
3244
 
 
3245
 
        // delete new non cfs files directly: they were never
3246
 
        // registered with IFD
3247
 
        synchronized(this) {
3248
 
          deleter.deleteNewFiles(info.files());
3249
 
        }
3250
 
        info.setUseCompoundFile(true);
3251
 
      }
3252
 
 
3253
 
      // Register the new segment
3254
 
      synchronized(this) {
3255
 
        if (stopMerges) {
3256
 
          deleter.deleteNewFiles(info.files());
3257
 
          return;
3258
 
        }
3259
 
        ensureOpen();
3260
 
        segmentInfos.add(info);
3261
 
        checkpoint();
3262
 
      }
3263
 
      
3264
 
    } catch (OutOfMemoryError oom) {
3265
 
      handleOOM(oom, "addIndexes(IndexReader...)");
3266
 
    }
3267
 
  }
3268
 
 
3269
 
  /** Copies the segment into the IndexWriter's directory, as a compound segment. */
3270
 
  private void copySegmentIntoCFS(SegmentInfo info, String segName) throws IOException {
3271
 
    String segFileName = IndexFileNames.segmentFileName(segName, IndexFileNames.COMPOUND_FILE_EXTENSION);
3272
 
    Collection<String> files = info.files();
3273
 
    CompoundFileWriter cfsWriter = new CompoundFileWriter(directory, segFileName);
3274
 
    for (String file : files) {
3275
 
      String newFileName = segName + IndexFileNames.stripSegmentName(file);
3276
 
      if (!IndexFileNames.matchesExtension(file, IndexFileNames.DELETES_EXTENSION)
3277
 
          && !IndexFileNames.isSeparateNormsFile(file)) {
3278
 
        cfsWriter.addFile(file, info.dir);
3279
 
      } else {
3280
 
        assert !directory.fileExists(newFileName): "file \"" + newFileName + "\" already exists";
3281
 
        info.dir.copy(directory, file, newFileName);
3282
 
      }
3283
 
    }
3284
 
    
3285
 
    // Create the .cfs
3286
 
    cfsWriter.close();
3287
 
    
3288
 
    info.dir = directory;
3289
 
    info.name = segName;
3290
 
    info.setUseCompoundFile(true);
3291
 
  }
3292
 
  
3293
 
  /** Copies the segment files as-is into the IndexWriter's directory. */
3294
 
  private void copySegmentAsIs(SegmentInfo info, String segName,
3295
 
      Map<String, String> dsNames, Set<String> dsFilesCopied)
3296
 
      throws IOException {
3297
 
    // Determine if the doc store of this segment needs to be copied. It's
3298
 
    // only relevant for segments that share doc store with others,
3299
 
    // because the DS might have been copied already, in which case we
3300
 
    // just want to update the DS name of this SegmentInfo.
3301
 
    // NOTE: pre-3x segments include a null DSName if they don't share doc
3302
 
    // store. The following code ensures we don't accidentally insert
3303
 
    // 'null' to the map.
3304
 
    String dsName = info.getDocStoreSegment();
3305
 
    final String newDsName;
3306
 
    if (dsName != null) {
3307
 
      if (dsNames.containsKey(dsName)) {
3308
 
        newDsName = dsNames.get(dsName);
3309
 
      } else {
3310
 
        dsNames.put(dsName, segName);
3311
 
        newDsName = segName;
3312
 
      }
3313
 
    } else {
3314
 
      newDsName = segName;
3315
 
    }
3316
 
    
3317
 
    // Copy the segment files
3318
 
    for (String file: info.files()) {
3319
 
      final String newFileName;
3320
 
      if (IndexFileNames.isDocStoreFile(file)) {
3321
 
        newFileName = newDsName + IndexFileNames.stripSegmentName(file);
3322
 
        if (dsFilesCopied.contains(newFileName)) {
3323
 
          continue;
3324
 
        }
3325
 
        dsFilesCopied.add(newFileName);
3326
 
      } else {
3327
 
        newFileName = segName + IndexFileNames.stripSegmentName(file);
3328
 
      }
3329
 
      
3330
 
      assert !directory.fileExists(newFileName): "file \"" + newFileName + "\" already exists";
3331
 
      info.dir.copy(directory, file, newFileName);
3332
 
    }
3333
 
    
3334
 
    info.setDocStore(info.getDocStoreOffset(), newDsName, info.getDocStoreIsCompoundFile());
3335
 
    info.dir = directory;
3336
 
    info.name = segName;
3337
 
  }
3338
 
  
3339
 
  /**
3340
 
   * A hook for extending classes to execute operations after pending added and
3341
 
   * deleted documents have been flushed to the Directory but before the change
3342
 
   * is committed (new segments_N file written).
3343
 
   */
3344
 
  protected void doAfterFlush() throws IOException {}
3345
 
 
3346
 
  /**
3347
 
   * A hook for extending classes to execute operations before pending added and
3348
 
   * deleted documents are flushed to the Directory.
3349
 
   */
3350
 
  protected void doBeforeFlush() throws IOException {}
3351
 
 
3352
 
  /** Expert: prepare for commit.
3353
 
   *
3354
 
   * <p><b>NOTE</b>: if this method hits an OutOfMemoryError
3355
 
   * you should immediately close the writer.  See <a
3356
 
   * href="#OOME">above</a> for details.</p>
3357
 
   *
3358
 
   * @see #prepareCommit(Map) */
3359
 
  public final void prepareCommit() throws CorruptIndexException, IOException {
3360
 
    ensureOpen();
3361
 
    prepareCommit(null);
3362
 
  }
3363
 
 
3364
 
  /** <p>Expert: prepare for commit, specifying
3365
 
   *  commitUserData Map (String -> String).  This does the
3366
 
   *  first phase of 2-phase commit. This method does all
3367
 
   *  steps necessary to commit changes since this writer
3368
 
   *  was opened: flushes pending added and deleted docs,
3369
 
   *  syncs the index files, writes most of next segments_N
3370
 
   *  file.  After calling this you must call either {@link
3371
 
   *  #commit()} to finish the commit, or {@link
3372
 
   *  #rollback()} to revert the commit and undo all changes
3373
 
   *  done since the writer was opened.</p>
3374
 
   * 
3375
 
   *  You can also just call {@link #commit(Map)} directly
3376
 
   *  without prepareCommit first in which case that method
3377
 
   *  will internally call prepareCommit.
3378
 
   *
3379
 
   *  <p><b>NOTE</b>: if this method hits an OutOfMemoryError
3380
 
   *  you should immediately close the writer.  See <a
3381
 
   *  href="#OOME">above</a> for details.</p>
3382
 
   *
3383
 
   *  @param commitUserData Opaque Map (String->String)
3384
 
   *  that's recorded into the segments file in the index,
3385
 
   *  and retrievable by {@link
3386
 
   *  IndexReader#getCommitUserData}.  Note that when
3387
 
   *  IndexWriter commits itself during {@link #close}, the
3388
 
   *  commitUserData is unchanged (just carried over from
3389
 
   *  the prior commit).  If this is null then the previous
3390
 
   *  commitUserData is kept.  Also, the commitUserData will
3391
 
   *  only "stick" if there are actually changes in the
3392
 
   *  index to commit.
3393
 
   */
3394
 
  public final void prepareCommit(Map<String, String> commitUserData)
3395
 
      throws CorruptIndexException, IOException {
3396
 
    ensureOpen(false);
3397
 
 
3398
 
    if (hitOOM) {
3399
 
      throw new IllegalStateException(
3400
 
          "this writer hit an OutOfMemoryError; cannot commit");
3401
 
    }
3402
 
 
3403
 
    if (pendingCommit != null)
3404
 
      throw new IllegalStateException(
3405
 
          "prepareCommit was already called with no corresponding call to commit");
3406
 
 
3407
 
    if (infoStream != null)
3408
 
      message("prepareCommit: flush");
3409
 
 
3410
 
    ensureOpen(false);
3411
 
    boolean anySegmentsFlushed = false;
3412
 
    SegmentInfos toCommit = null;
3413
 
    boolean success = false;
3414
 
    try {
3415
 
      try {
3416
 
        synchronized (this) {
3417
 
          anySegmentsFlushed = doFlush(true);
3418
 
          readerPool.commit(segmentInfos);
3419
 
          toCommit = (SegmentInfos) segmentInfos.clone();
3420
 
          pendingCommitChangeCount = changeCount;
3421
 
          // This protects the segmentInfos we are now going
3422
 
          // to commit. This is important in case, eg, while
3423
 
          // we are trying to sync all referenced files, a
3424
 
          // merge completes which would otherwise have
3425
 
          // removed the files we are now syncing.
3426
 
          deleter.incRef(toCommit, false);
3427
 
        }
3428
 
        success = true;
3429
 
      } finally {
3430
 
        if (!success && infoStream != null) {
3431
 
          message("hit exception during prepareCommit");
3432
 
        }
3433
 
        doAfterFlush();
3434
 
      }
3435
 
    } catch (OutOfMemoryError oom) {
3436
 
      handleOOM(oom, "prepareCommit");
3437
 
    }
3438
 
 
3439
 
    success = false;
3440
 
    try {
3441
 
      if (anySegmentsFlushed) {
3442
 
        maybeMerge();
3443
 
      } 
3444
 
      success = true;
3445
 
    } finally {
3446
 
      if (!success) {
3447
 
        synchronized (this) {
3448
 
          deleter.decRef(toCommit);
3449
 
        }
3450
 
      }
3451
 
    }
3452
 
 
3453
 
    startCommit(toCommit, commitUserData);
3454
 
  }
3455
 
 
3456
 
  // Used only by commit, below; lock order is commitLock -> IW
3457
 
  private final Object commitLock = new Object();
3458
 
 
3459
 
  /**
3460
 
   * <p>Commits all pending changes (added & deleted
3461
 
   * documents, segment merges, added
3462
 
   * indexes, etc.) to the index, and syncs all referenced
3463
 
   * index files, such that a reader will see the changes
3464
 
   * and the index updates will survive an OS or machine
3465
 
   * crash or power loss.  Note that this does not wait for
3466
 
   * any running background merges to finish.  This may be a
3467
 
   * costly operation, so you should test the cost in your
3468
 
   * application and do it only when really necessary.</p>
3469
 
   *
3470
 
   * <p> Note that this operation calls Directory.sync on
3471
 
   * the index files.  That call should not return until the
3472
 
   * file contents & metadata are on stable storage.  For
3473
 
   * FSDirectory, this calls the OS's fsync.  But, beware:
3474
 
   * some hardware devices may in fact cache writes even
3475
 
   * during fsync, and return before the bits are actually
3476
 
   * on stable storage, to give the appearance of faster
3477
 
   * performance.  If you have such a device, and it does
3478
 
   * not have a battery backup (for example) then on power
3479
 
   * loss it may still lose data.  Lucene cannot guarantee
3480
 
   * consistency on such devices.  </p>
3481
 
   *
3482
 
   * <p><b>NOTE</b>: if this method hits an OutOfMemoryError
3483
 
   * you should immediately close the writer.  See <a
3484
 
   * href="#OOME">above</a> for details.</p>
3485
 
   *
3486
 
   * @see #prepareCommit
3487
 
   * @see #commit(Map)
3488
 
   */
3489
 
  public final void commit() throws CorruptIndexException, IOException {
3490
 
    commit(null);
3491
 
  }
3492
 
 
3493
 
  /** Commits all changes to the index, specifying a
3494
 
   *  commitUserData Map (String -> String).  This just
3495
 
   *  calls {@link #prepareCommit(Map)} (if you didn't
3496
 
   *  already call it) and then {@link #finishCommit}.
3497
 
   *
3498
 
   * <p><b>NOTE</b>: if this method hits an OutOfMemoryError
3499
 
   * you should immediately close the writer.  See <a
3500
 
   * href="#OOME">above</a> for details.</p>
3501
 
   */
3502
 
  public final void commit(Map<String,String> commitUserData) throws CorruptIndexException, IOException {
3503
 
 
3504
 
    ensureOpen();
3505
 
 
3506
 
    commitInternal(commitUserData);
3507
 
  }
3508
 
 
3509
 
  private final void commitInternal(Map<String,String> commitUserData) throws CorruptIndexException, IOException {
3510
 
 
3511
 
    if (infoStream != null) {
3512
 
      message("commit: start");
3513
 
    }
3514
 
 
3515
 
    synchronized(commitLock) {
3516
 
      if (infoStream != null) {
3517
 
        message("commit: enter lock");
3518
 
      }
3519
 
 
3520
 
      if (pendingCommit == null) {
3521
 
        if (infoStream != null) {
3522
 
          message("commit: now prepare");
3523
 
        }
3524
 
        prepareCommit(commitUserData);
3525
 
      } else if (infoStream != null) {
3526
 
        message("commit: already prepared");
3527
 
      }
3528
 
 
3529
 
      finishCommit();
3530
 
    }
3531
 
  }
3532
 
 
3533
 
  private synchronized final void finishCommit() throws CorruptIndexException, IOException {
3534
 
 
3535
 
    if (pendingCommit != null) {
3536
 
      try {
3537
 
        if (infoStream != null)
3538
 
          message("commit: pendingCommit != null");
3539
 
        pendingCommit.finishCommit(directory);
3540
 
        if (infoStream != null)
3541
 
          message("commit: wrote segments file \"" + pendingCommit.getCurrentSegmentFileName() + "\"");
3542
 
        lastCommitChangeCount = pendingCommitChangeCount;
3543
 
        segmentInfos.updateGeneration(pendingCommit);
3544
 
        segmentInfos.setUserData(pendingCommit.getUserData());
3545
 
        rollbackSegments = pendingCommit.createBackupSegmentInfos(true);
3546
 
        deleter.checkpoint(pendingCommit, true);
3547
 
      } finally {
3548
 
        // Matches the incRef done in startCommit:
3549
 
        deleter.decRef(pendingCommit);
3550
 
        pendingCommit = null;
3551
 
        notifyAll();
3552
 
      }
3553
 
 
3554
 
    } else if (infoStream != null) {
3555
 
      message("commit: pendingCommit == null; skip");
3556
 
    }
3557
 
 
3558
 
    if (infoStream != null) {
3559
 
      message("commit: done");
3560
 
    }
3561
 
  }
3562
 
 
3563
 
  /** NOTE: flushDocStores is ignored now (hardwired to
3564
 
   *  true); this method is only here for backwards
3565
 
   *  compatibility */
3566
 
  protected final void flush(boolean triggerMerge, boolean flushDocStores, boolean flushDeletes) throws CorruptIndexException, IOException {
3567
 
    flush(triggerMerge, flushDeletes);
3568
 
  }
3569
 
 
3570
 
  /**
3571
 
   * Flush all in-memory buffered updates (adds and deletes)
3572
 
   * to the Directory.
3573
 
   * @param triggerMerge if true, we may merge segments (if
3574
 
   *  deletes or docs were flushed) if necessary
3575
 
   * @param applyAllDeletes whether pending deletes should also
3576
 
   */
3577
 
  protected final void flush(boolean triggerMerge, boolean applyAllDeletes) throws CorruptIndexException, IOException {
3578
 
 
3579
 
    // NOTE: this method cannot be sync'd because
3580
 
    // maybeMerge() in turn calls mergeScheduler.merge which
3581
 
    // in turn can take a long time to run and we don't want
3582
 
    // to hold the lock for that.  In the case of
3583
 
    // ConcurrentMergeScheduler this can lead to deadlock
3584
 
    // when it stalls due to too many running merges.
3585
 
 
3586
 
    // We can be called during close, when closing==true, so we must pass false to ensureOpen:
3587
 
    ensureOpen(false);
3588
 
    if (doFlush(applyAllDeletes) && triggerMerge) {
3589
 
      maybeMerge();
3590
 
    }
3591
 
  }
3592
 
 
3593
 
  // TODO: this method should not have to be entirely
3594
 
  // synchronized, ie, merges should be allowed to commit
3595
 
  // even while a flush is happening
3596
 
  private synchronized boolean doFlush(boolean applyAllDeletes) throws CorruptIndexException, IOException {
3597
 
 
3598
 
    if (hitOOM) {
3599
 
      throw new IllegalStateException("this writer hit an OutOfMemoryError; cannot flush");
3600
 
    }
3601
 
 
3602
 
    doBeforeFlush();
3603
 
 
3604
 
    assert testPoint("startDoFlush");
3605
 
 
3606
 
    // We may be flushing because it was triggered by doc
3607
 
    // count, del count, ram usage (in which case flush
3608
 
    // pending is already set), or we may be flushing
3609
 
    // due to external event eg getReader or commit is
3610
 
    // called (in which case we now set it, and this will
3611
 
    // pause all threads):
3612
 
    flushControl.setFlushPendingNoWait("explicit flush");
3613
 
 
3614
 
    boolean success = false;
3615
 
 
3616
 
    try {
3617
 
 
3618
 
      if (infoStream != null) {
3619
 
        message("  start flush: applyAllDeletes=" + applyAllDeletes);
3620
 
        message("  index before flush " + segString());
3621
 
      }
3622
 
    
3623
 
      final SegmentInfo newSegment = docWriter.flush(this, deleter, mergePolicy, segmentInfos);
3624
 
      if (newSegment != null) {
3625
 
        setDiagnostics(newSegment, "flush");
3626
 
        segmentInfos.add(newSegment);
3627
 
        checkpoint();
3628
 
      }
3629
 
 
3630
 
      if (!applyAllDeletes) {
3631
 
        // If deletes alone are consuming > 1/2 our RAM
3632
 
        // buffer, force them all to apply now. This is to
3633
 
        // prevent too-frequent flushing of a long tail of
3634
 
        // tiny segments:
3635
 
        if (flushControl.getFlushDeletes() ||
3636
 
            (config.getRAMBufferSizeMB() != IndexWriterConfig.DISABLE_AUTO_FLUSH &&
3637
 
             bufferedDeletesStream.bytesUsed() > (1024*1024*config.getRAMBufferSizeMB()/2))) {
3638
 
          applyAllDeletes = true;
3639
 
          if (infoStream != null) {
3640
 
            message("force apply deletes bytesUsed=" + bufferedDeletesStream.bytesUsed() + " vs ramBuffer=" + (1024*1024*config.getRAMBufferSizeMB()));
3641
 
          }
3642
 
        }
3643
 
      }
3644
 
 
3645
 
      if (applyAllDeletes) {
3646
 
        if (infoStream != null) {
3647
 
          message("apply all deletes during flush");
3648
 
        }
3649
 
        
3650
 
        flushDeletesCount.incrementAndGet();
3651
 
        final BufferedDeletesStream.ApplyDeletesResult result = bufferedDeletesStream
3652
 
          .applyDeletes(readerPool, segmentInfos.asList());
3653
 
        if (result.anyDeletes) {
3654
 
          checkpoint();
3655
 
        }
3656
 
        if (!keepFullyDeletedSegments && result.allDeleted != null) {
3657
 
          if (infoStream != null) {
3658
 
            message("drop 100% deleted segments: " + result.allDeleted);
3659
 
          }
3660
 
          for (SegmentInfo info : result.allDeleted) {
3661
 
            // If a merge has already registered for this
3662
 
            // segment, we leave it in the readerPool; the
3663
 
            // merge will skip merging it and will then drop
3664
 
            // it once it's done:
3665
 
            if (!mergingSegments.contains(info)) {
3666
 
              segmentInfos.remove(info);
3667
 
              if (readerPool != null) {
3668
 
                readerPool.drop(info);
3669
 
              }
3670
 
            }
3671
 
          }
3672
 
          checkpoint();
3673
 
        }
3674
 
        bufferedDeletesStream.prune(segmentInfos);
3675
 
 
3676
 
        assert !bufferedDeletesStream.any();
3677
 
        flushControl.clearDeletes();
3678
 
      } else if (infoStream != null) {
3679
 
        message("don't apply deletes now delTermCount=" + bufferedDeletesStream.numTerms() + " bytesUsed=" + bufferedDeletesStream.bytesUsed());
3680
 
      }
3681
 
      
3682
 
 
3683
 
      doAfterFlush();
3684
 
      flushCount.incrementAndGet();
3685
 
 
3686
 
      success = true;
3687
 
 
3688
 
      return newSegment != null;
3689
 
 
3690
 
    } catch (OutOfMemoryError oom) {
3691
 
      handleOOM(oom, "doFlush");
3692
 
      // never hit
3693
 
      return false;
3694
 
    } finally {
3695
 
      flushControl.clearFlushPending();
3696
 
      if (!success && infoStream != null)
3697
 
        message("hit exception during flush");
3698
 
    }
3699
 
  }
3700
 
 
3701
 
  /** Expert:  Return the total size of all index files currently cached in memory.
3702
 
   * Useful for size management with flushRamDocs()
3703
 
   */
3704
 
  public final long ramSizeInBytes() {
3705
 
    ensureOpen();
3706
 
    return docWriter.bytesUsed() + bufferedDeletesStream.bytesUsed();
3707
 
  }
3708
 
 
3709
 
  /** Expert:  Return the number of documents currently
3710
 
   *  buffered in RAM. */
3711
 
  public final synchronized int numRamDocs() {
3712
 
    ensureOpen();
3713
 
    return docWriter.getNumDocs();
3714
 
  }
3715
 
 
3716
 
  private void ensureValidMerge(MergePolicy.OneMerge merge) throws IOException {
3717
 
    for(SegmentInfo info : merge.segments) {
3718
 
      if (!segmentInfos.contains(info)) {
3719
 
        throw new MergePolicy.MergeException("MergePolicy selected a segment (" + info.name + ") that is not in the current index " + segString(), directory);
3720
 
      }
3721
 
    }
3722
 
  }
3723
 
 
3724
 
  /** Carefully merges deletes for the segments we just
3725
 
   *  merged.  This is tricky because, although merging will
3726
 
   *  clear all deletes (compacts the documents), new
3727
 
   *  deletes may have been flushed to the segments since
3728
 
   *  the merge was started.  This method "carries over"
3729
 
   *  such new deletes onto the newly merged segment, and
3730
 
   *  saves the resulting deletes file (incrementing the
3731
 
   *  delete generation for merge.info).  If no deletes were
3732
 
   *  flushed, no new deletes file is saved. */
3733
 
  synchronized private void commitMergedDeletes(MergePolicy.OneMerge merge, SegmentReader mergedReader) throws IOException {
3734
 
 
3735
 
    assert testPoint("startCommitMergeDeletes");
3736
 
 
3737
 
    final List<SegmentInfo> sourceSegments = merge.segments;
3738
 
 
3739
 
    if (infoStream != null)
3740
 
      message("commitMergeDeletes " + merge.segString(directory));
3741
 
 
3742
 
    // Carefully merge deletes that occurred after we
3743
 
    // started merging:
3744
 
    int docUpto = 0;
3745
 
    int delCount = 0;
3746
 
    long minGen = Long.MAX_VALUE;
3747
 
 
3748
 
    for(int i=0; i < sourceSegments.size(); i++) {
3749
 
      SegmentInfo info = sourceSegments.get(i);
3750
 
      minGen = Math.min(info.getBufferedDeletesGen(), minGen);
3751
 
      int docCount = info.docCount;
3752
 
      final SegmentReader previousReader = merge.readerClones.get(i);
3753
 
      if (previousReader == null) {
3754
 
        // Reader was skipped because it was 100% deletions
3755
 
        continue;
3756
 
      }
3757
 
      final SegmentReader currentReader = merge.readers.get(i);
3758
 
      if (previousReader.hasDeletions()) {
3759
 
 
3760
 
        // There were deletes on this segment when the merge
3761
 
        // started.  The merge has collapsed away those
3762
 
        // deletes, but, if new deletes were flushed since
3763
 
        // the merge started, we must now carefully keep any
3764
 
        // newly flushed deletes but mapping them to the new
3765
 
        // docIDs.
3766
 
 
3767
 
        if (currentReader.numDeletedDocs() > previousReader.numDeletedDocs()) {
3768
 
          // This means this segment has had new deletes
3769
 
          // committed since we started the merge, so we
3770
 
          // must merge them:
3771
 
          for(int j=0;j<docCount;j++) {
3772
 
            if (previousReader.isDeleted(j))
3773
 
              assert currentReader.isDeleted(j);
3774
 
            else {
3775
 
              if (currentReader.isDeleted(j)) {
3776
 
                mergedReader.doDelete(docUpto);
3777
 
                delCount++;
3778
 
              }
3779
 
              docUpto++;
3780
 
            }
3781
 
          }
3782
 
        } else {
3783
 
          docUpto += docCount - previousReader.numDeletedDocs();
3784
 
        }
3785
 
      } else if (currentReader.hasDeletions()) {
3786
 
        // This segment had no deletes before but now it
3787
 
        // does:
3788
 
        for(int j=0; j<docCount; j++) {
3789
 
          if (currentReader.isDeleted(j)) {
3790
 
            mergedReader.doDelete(docUpto);
3791
 
            delCount++;
3792
 
          }
3793
 
          docUpto++;
3794
 
        }
3795
 
      } else
3796
 
        // No deletes before or after
3797
 
        docUpto += info.docCount;
3798
 
    }
3799
 
 
3800
 
    assert mergedReader.numDeletedDocs() == delCount;
3801
 
 
3802
 
    mergedReader.hasChanges = delCount > 0;
3803
 
 
3804
 
    // If new deletes were applied while we were merging
3805
 
    // (which happens if eg commit() or getReader() is
3806
 
    // called during our merge), then it better be the case
3807
 
    // that the delGen has increased for all our merged
3808
 
    // segments:
3809
 
    assert !mergedReader.hasChanges || minGen > mergedReader.getSegmentInfo().getBufferedDeletesGen();
3810
 
 
3811
 
    mergedReader.getSegmentInfo().setBufferedDeletesGen(minGen);
3812
 
  }
3813
 
 
3814
 
  synchronized private boolean commitMerge(MergePolicy.OneMerge merge, SegmentReader mergedReader) throws IOException {
3815
 
 
3816
 
    assert testPoint("startCommitMerge");
3817
 
 
3818
 
    if (hitOOM) {
3819
 
      throw new IllegalStateException("this writer hit an OutOfMemoryError; cannot complete merge");
3820
 
    }
3821
 
 
3822
 
    if (infoStream != null)
3823
 
      message("commitMerge: " + merge.segString(directory) + " index=" + segString());
3824
 
 
3825
 
    assert merge.registerDone;
3826
 
 
3827
 
    // If merge was explicitly aborted, or, if rollback() or
3828
 
    // rollbackTransaction() had been called since our merge
3829
 
    // started (which results in an unqualified
3830
 
    // deleter.refresh() call that will remove any index
3831
 
    // file that current segments does not reference), we
3832
 
    // abort this merge
3833
 
    if (merge.isAborted()) {
3834
 
      if (infoStream != null)
3835
 
        message("commitMerge: skipping merge " + merge.segString(directory) + ": it was aborted");
3836
 
      return false;
3837
 
    }
3838
 
 
3839
 
    commitMergedDeletes(merge, mergedReader);
3840
 
      
3841
 
    // If the doc store we are using has been closed and
3842
 
    // is in now compound format (but wasn't when we
3843
 
    // started), then we will switch to the compound
3844
 
    // format as well:
3845
 
 
3846
 
    assert !segmentInfos.contains(merge.info);
3847
 
 
3848
 
    final boolean allDeleted = mergedReader.numDocs() == 0;
3849
 
 
3850
 
    if (infoStream != null && allDeleted) {
3851
 
      message("merged segment " + merge.info + " is 100% deleted" +  (keepFullyDeletedSegments ? "" : "; skipping insert"));
3852
 
    }
3853
 
 
3854
 
    final boolean dropSegment = allDeleted && !keepFullyDeletedSegments;
3855
 
    segmentInfos.applyMergeChanges(merge, dropSegment);
3856
 
    
3857
 
    if (dropSegment) {
3858
 
      readerPool.drop(merge.info);
3859
 
    }
3860
 
    
3861
 
    if (infoStream != null) {
3862
 
      message("after commit: " + segString());
3863
 
    }
3864
 
 
3865
 
    closeMergeReaders(merge, false);
3866
 
 
3867
 
    // Must note the change to segmentInfos so any commits
3868
 
    // in-flight don't lose it:
3869
 
    checkpoint();
3870
 
 
3871
 
    // If the merged segments had pending changes, clear
3872
 
    // them so that they don't bother writing them to
3873
 
    // disk, updating SegmentInfo, etc.:
3874
 
    readerPool.clear(merge.segments);
3875
 
    
3876
 
    if (merge.maxNumSegments != -1) {
3877
 
      // cascade the forceMerge:
3878
 
      if (!segmentsToMerge.containsKey(merge.info)) {
3879
 
        segmentsToMerge.put(merge.info, Boolean.FALSE);
3880
 
      }
3881
 
    }
3882
 
    
3883
 
    return true;
3884
 
  }
3885
 
  
3886
 
  final private void handleMergeException(Throwable t, MergePolicy.OneMerge merge) throws IOException {
3887
 
 
3888
 
    if (infoStream != null) {
3889
 
      message("handleMergeException: merge=" + merge.segString(directory) + " exc=" + t);
3890
 
    }
3891
 
 
3892
 
    // Set the exception on the merge, so if
3893
 
    // forceMerge is waiting on us it sees the root
3894
 
    // cause exception:
3895
 
    merge.setException(t);
3896
 
    addMergeException(merge);
3897
 
 
3898
 
    if (t instanceof MergePolicy.MergeAbortedException) {
3899
 
      // We can ignore this exception (it happens when
3900
 
      // close(false) or rollback is called), unless the
3901
 
      // merge involves segments from external directories,
3902
 
      // in which case we must throw it so, for example, the
3903
 
      // rollbackTransaction code in addIndexes* is
3904
 
      // executed.
3905
 
      if (merge.isExternal)
3906
 
        throw (MergePolicy.MergeAbortedException) t;
3907
 
    } else if (t instanceof IOException)
3908
 
      throw (IOException) t;
3909
 
    else if (t instanceof RuntimeException)
3910
 
      throw (RuntimeException) t;
3911
 
    else if (t instanceof Error)
3912
 
      throw (Error) t;
3913
 
    else
3914
 
      // Should not get here
3915
 
      throw new RuntimeException(t);
3916
 
  }
3917
 
 
3918
 
  /**
3919
 
   * Merges the indicated segments, replacing them in the stack with a
3920
 
   * single segment.
3921
 
   * 
3922
 
   * @lucene.experimental
3923
 
   */
3924
 
  public void merge(MergePolicy.OneMerge merge)
3925
 
    throws CorruptIndexException, IOException {
3926
 
 
3927
 
    boolean success = false;
3928
 
 
3929
 
    final long t0 = System.currentTimeMillis();
3930
 
    //System.out.println(Thread.currentThread().getName() + ": merge start: size=" + (merge.estimatedMergeBytes/1024./1024.) + " MB\n  merge=" + merge.segString(directory) + "\n  idx=" + segString());
3931
 
 
3932
 
    try {
3933
 
      try {
3934
 
        try {
3935
 
          mergeInit(merge);
3936
 
 
3937
 
          if (infoStream != null)
3938
 
            message("now merge\n  merge=" + merge.segString(directory) + "\n  merge=" + merge + "\n  index=" + segString());
3939
 
 
3940
 
          mergeMiddle(merge);
3941
 
          mergeSuccess(merge);
3942
 
          success = true;
3943
 
        } catch (Throwable t) {
3944
 
          handleMergeException(t, merge);
3945
 
        }
3946
 
      } finally {
3947
 
        synchronized(this) {
3948
 
          mergeFinish(merge);
3949
 
 
3950
 
          if (!success) {
3951
 
            if (infoStream != null)
3952
 
              message("hit exception during merge");
3953
 
            if (merge.info != null && !segmentInfos.contains(merge.info))
3954
 
              deleter.refresh(merge.info.name);
3955
 
          }
3956
 
 
3957
 
          // This merge (and, generally, any change to the
3958
 
          // segments) may now enable new merges, so we call
3959
 
          // merge policy & update pending merges.
3960
 
          if (success && !merge.isAborted() && (merge.maxNumSegments != -1 || (!closed && !closing))) {
3961
 
            updatePendingMerges(merge.maxNumSegments);
3962
 
          }
3963
 
        }
3964
 
      }
3965
 
    } catch (OutOfMemoryError oom) {
3966
 
      handleOOM(oom, "merge");
3967
 
    }
3968
 
    if (infoStream != null && merge.info != null) {
3969
 
      message("merge time " + (System.currentTimeMillis()-t0) + " msec for " + merge.info.docCount + " docs");
3970
 
    }
3971
 
    //System.out.println(Thread.currentThread().getName() + ": merge end");
3972
 
  }
3973
 
 
3974
 
  /** Hook that's called when the specified merge is complete. */
3975
 
  void mergeSuccess(MergePolicy.OneMerge merge) {
3976
 
  }
3977
 
  
3978
 
  /** Checks whether this merge involves any segments
3979
 
   *  already participating in a merge.  If not, this merge
3980
 
   *  is "registered", meaning we record that its segments
3981
 
   *  are now participating in a merge, and true is
3982
 
   *  returned.  Else (the merge conflicts) false is
3983
 
   *  returned. */
3984
 
  final synchronized boolean registerMerge(MergePolicy.OneMerge merge) throws MergePolicy.MergeAbortedException, IOException {
3985
 
 
3986
 
    if (merge.registerDone)
3987
 
      return true;
3988
 
 
3989
 
    if (stopMerges) {
3990
 
      merge.abort();
3991
 
      throw new MergePolicy.MergeAbortedException("merge is aborted: " + merge.segString(directory));
3992
 
    }
3993
 
 
3994
 
    boolean isExternal = false;
3995
 
    for(SegmentInfo info : merge.segments) {
3996
 
      if (mergingSegments.contains(info)) {
3997
 
        return false;
3998
 
      }
3999
 
      if (!segmentInfos.contains(info)) {
4000
 
        return false;
4001
 
      }
4002
 
      if (info.dir != directory) {
4003
 
        isExternal = true;
4004
 
      }
4005
 
      if (segmentsToMerge.containsKey(info)) {
4006
 
        merge.maxNumSegments = mergeMaxNumSegments;
4007
 
      }
4008
 
    }
4009
 
 
4010
 
    ensureValidMerge(merge);
4011
 
 
4012
 
    pendingMerges.add(merge);
4013
 
 
4014
 
    if (infoStream != null)
4015
 
      message("add merge to pendingMerges: " + merge.segString(directory) + " [total " + pendingMerges.size() + " pending]");
4016
 
 
4017
 
    merge.mergeGen = mergeGen;
4018
 
    merge.isExternal = isExternal;
4019
 
 
4020
 
    // OK it does not conflict; now record that this merge
4021
 
    // is running (while synchronized) to avoid race
4022
 
    // condition where two conflicting merges from different
4023
 
    // threads, start
4024
 
    message("registerMerge merging=" + mergingSegments);
4025
 
    for(SegmentInfo info : merge.segments) {
4026
 
      message("registerMerge info=" + info);
4027
 
      mergingSegments.add(info);
4028
 
    }
4029
 
 
4030
 
    // Merge is now registered
4031
 
    merge.registerDone = true;
4032
 
    return true;
4033
 
  }
4034
 
 
4035
 
  /** Does initial setup for a merge, which is fast but holds
4036
 
   *  the synchronized lock on IndexWriter instance.  */
4037
 
  final synchronized void mergeInit(MergePolicy.OneMerge merge) throws IOException {
4038
 
    boolean success = false;
4039
 
    try {
4040
 
      _mergeInit(merge);
4041
 
      success = true;
4042
 
    } finally {
4043
 
      if (!success) {
4044
 
        if (infoStream != null) {
4045
 
          message("hit exception in mergeInit");
4046
 
        }
4047
 
        mergeFinish(merge);
4048
 
      }
4049
 
    }
4050
 
  }
4051
 
 
4052
 
  synchronized private void _mergeInit(MergePolicy.OneMerge merge) throws IOException {
4053
 
 
4054
 
    assert testPoint("startMergeInit");
4055
 
 
4056
 
    assert merge.registerDone;
4057
 
    assert merge.maxNumSegments == -1 || merge.maxNumSegments > 0;
4058
 
 
4059
 
    if (hitOOM) {
4060
 
      throw new IllegalStateException("this writer hit an OutOfMemoryError; cannot merge");
4061
 
    }
4062
 
 
4063
 
    // TODO: is there any perf benefit to sorting
4064
 
    // merged segments?  eg biggest to smallest?
4065
 
 
4066
 
    if (merge.info != null)
4067
 
      // mergeInit already done
4068
 
      return;
4069
 
 
4070
 
    if (merge.isAborted())
4071
 
      return;
4072
 
 
4073
 
    boolean hasVectors = false;
4074
 
    for (SegmentInfo sourceSegment : merge.segments) {
4075
 
      if (sourceSegment.getHasVectors()) {
4076
 
        hasVectors = true;
4077
 
      }
4078
 
    }
4079
 
 
4080
 
    // Bind a new segment name here so even with
4081
 
    // ConcurrentMergePolicy we keep deterministic segment
4082
 
    // names.
4083
 
    merge.info = new SegmentInfo(newSegmentName(), 0, directory, false, true, false, hasVectors);
4084
 
 
4085
 
    // Lock order: IW -> BD
4086
 
    final BufferedDeletesStream.ApplyDeletesResult result = bufferedDeletesStream.applyDeletes(readerPool, merge.segments);
4087
 
 
4088
 
    if (result.anyDeletes) {
4089
 
      checkpoint();
4090
 
    }
4091
 
 
4092
 
    if (!keepFullyDeletedSegments && result.allDeleted != null) {
4093
 
      if (infoStream != null) {
4094
 
        message("drop 100% deleted segments: " + result.allDeleted);
4095
 
      }
4096
 
      for(SegmentInfo info : result.allDeleted) {
4097
 
        segmentInfos.remove(info);
4098
 
        if (merge.segments.contains(info)) {
4099
 
          mergingSegments.remove(info);
4100
 
          merge.segments.remove(info);
4101
 
        }
4102
 
      }
4103
 
      if (readerPool != null) {
4104
 
        readerPool.drop(result.allDeleted);
4105
 
      }
4106
 
      checkpoint();
4107
 
    }
4108
 
 
4109
 
    merge.info.setBufferedDeletesGen(result.gen);
4110
 
 
4111
 
    // Lock order: IW -> BD
4112
 
    bufferedDeletesStream.prune(segmentInfos);
4113
 
 
4114
 
    Map<String,String> details = new HashMap<String,String>();
4115
 
    details.put("mergeMaxNumSegments", ""+merge.maxNumSegments);
4116
 
    details.put("mergeFactor", Integer.toString(merge.segments.size()));
4117
 
    setDiagnostics(merge.info, "merge", details);
4118
 
 
4119
 
    if (infoStream != null) {
4120
 
      message("merge seg=" + merge.info.name);
4121
 
    }
4122
 
 
4123
 
    assert merge.estimatedMergeBytes == 0;
4124
 
    for(SegmentInfo info : merge.segments) {
4125
 
      if (info.docCount > 0) {
4126
 
        final int delCount = numDeletedDocs(info);
4127
 
        assert delCount <= info.docCount;
4128
 
        final double delRatio = ((double) delCount)/info.docCount;
4129
 
        merge.estimatedMergeBytes += info.sizeInBytes(true) * (1.0 - delRatio);
4130
 
      }
4131
 
    }
4132
 
 
4133
 
    // TODO: I think this should no longer be needed (we
4134
 
    // now build CFS before adding segment to the infos);
4135
 
    // however, on removing it, tests fail for some reason!
4136
 
 
4137
 
    // Also enroll the merged segment into mergingSegments;
4138
 
    // this prevents it from getting selected for a merge
4139
 
    // after our merge is done but while we are building the
4140
 
    // CFS:
4141
 
    mergingSegments.add(merge.info);
4142
 
  }
4143
 
 
4144
 
  private void setDiagnostics(SegmentInfo info, String source) {
4145
 
    setDiagnostics(info, source, null);
4146
 
  }
4147
 
 
4148
 
  private void setDiagnostics(SegmentInfo info, String source, Map<String,String> details) {
4149
 
    Map<String,String> diagnostics = new HashMap<String,String>();
4150
 
    diagnostics.put("source", source);
4151
 
    diagnostics.put("lucene.version", Constants.LUCENE_VERSION);
4152
 
    diagnostics.put("os", Constants.OS_NAME);
4153
 
    diagnostics.put("os.arch", Constants.OS_ARCH);
4154
 
    diagnostics.put("os.version", Constants.OS_VERSION);
4155
 
    diagnostics.put("java.version", Constants.JAVA_VERSION);
4156
 
    diagnostics.put("java.vendor", Constants.JAVA_VENDOR);
4157
 
    if (details != null) {
4158
 
      diagnostics.putAll(details);
4159
 
    }
4160
 
    info.setDiagnostics(diagnostics);
4161
 
  }
4162
 
 
4163
 
  /** Does fininishing for a merge, which is fast but holds
4164
 
   *  the synchronized lock on IndexWriter instance. */
4165
 
  final synchronized void mergeFinish(MergePolicy.OneMerge merge) throws IOException {
4166
 
    
4167
 
    // forceMerge, addIndexes or finishMerges may be waiting
4168
 
    // on merges to finish.
4169
 
    notifyAll();
4170
 
 
4171
 
    // It's possible we are called twice, eg if there was an
4172
 
    // exception inside mergeInit
4173
 
    if (merge.registerDone) {
4174
 
      final List<SegmentInfo> sourceSegments = merge.segments;
4175
 
      for(SegmentInfo info : sourceSegments) {
4176
 
        mergingSegments.remove(info);
4177
 
      }
4178
 
      // TODO: if we remove the add in _mergeInit, we should
4179
 
      // also remove this:
4180
 
      mergingSegments.remove(merge.info);
4181
 
      merge.registerDone = false;
4182
 
    }
4183
 
 
4184
 
    runningMerges.remove(merge);
4185
 
  }
4186
 
 
4187
 
  private final synchronized void closeMergeReaders(MergePolicy.OneMerge merge, boolean suppressExceptions) throws IOException {
4188
 
    final int numSegments = merge.readers.size();
4189
 
    Throwable th = null;
4190
 
    
4191
 
    boolean anyChanges = false;
4192
 
    boolean drop = !suppressExceptions;
4193
 
    for (int i = 0; i < numSegments; i++) {
4194
 
      if (merge.readers.get(i) != null) {
4195
 
        try {
4196
 
          anyChanges |= readerPool.release(merge.readers.get(i), drop);
4197
 
        } catch (Throwable t) {
4198
 
          if (th == null) {
4199
 
            th = t;
4200
 
          }
4201
 
        }
4202
 
        merge.readers.set(i, null);
4203
 
      }
4204
 
      
4205
 
      if (i < merge.readerClones.size() && merge.readerClones.get(i) != null) {
4206
 
        try {
4207
 
          merge.readerClones.get(i).close();
4208
 
        } catch (Throwable t) {
4209
 
          if (th == null) {
4210
 
            th = t;
4211
 
          }
4212
 
        }
4213
 
        // This was a private clone and we had the
4214
 
        // only reference
4215
 
        assert merge.readerClones.get(i).getRefCount() == 0: "refCount should be 0 but is " + merge.readerClones.get(i).getRefCount();
4216
 
        merge.readerClones.set(i, null);
4217
 
      }
4218
 
    }
4219
 
    
4220
 
    if (suppressExceptions && anyChanges) {
4221
 
      checkpoint();
4222
 
    }
4223
 
    
4224
 
    // If any error occured, throw it.
4225
 
    if (!suppressExceptions && th != null) {
4226
 
      if (th instanceof IOException) throw (IOException) th;
4227
 
      if (th instanceof RuntimeException) throw (RuntimeException) th;
4228
 
      if (th instanceof Error) throw (Error) th;
4229
 
      throw new RuntimeException(th);
4230
 
    }
4231
 
  }
4232
 
 
4233
 
  /** Does the actual (time-consuming) work of the merge,
4234
 
   *  but without holding synchronized lock on IndexWriter
4235
 
   *  instance */
4236
 
  final private int mergeMiddle(MergePolicy.OneMerge merge) 
4237
 
    throws CorruptIndexException, IOException {
4238
 
    
4239
 
    merge.checkAborted(directory);
4240
 
 
4241
 
    final String mergedName = merge.info.name;
4242
 
    
4243
 
    int mergedDocCount = 0;
4244
 
 
4245
 
    List<SegmentInfo> sourceSegments = merge.segments;
4246
 
 
4247
 
    SegmentMerger merger = new SegmentMerger(directory, config.getTermIndexInterval(), mergedName, merge,
4248
 
                                             payloadProcessorProvider,
4249
 
                                             ((FieldInfos) docWriter.getFieldInfos().clone()));
4250
 
 
4251
 
    if (infoStream != null) {
4252
 
      message("merging " + merge.segString(directory) + " mergeVectors=" + merge.info.getHasVectors());
4253
 
    }
4254
 
 
4255
 
    merge.readers = new ArrayList<SegmentReader>();
4256
 
    merge.readerClones = new ArrayList<SegmentReader>();
4257
 
 
4258
 
    // This is try/finally to make sure merger's readers are
4259
 
    // closed:
4260
 
    boolean success = false;
4261
 
    try {
4262
 
      int totDocCount = 0;
4263
 
      int segUpto = 0;
4264
 
      while(segUpto < sourceSegments.size()) {
4265
 
 
4266
 
        final SegmentInfo info = sourceSegments.get(segUpto);
4267
 
 
4268
 
        // Hold onto the "live" reader; we will use this to
4269
 
        // commit merged deletes
4270
 
        final SegmentReader reader = readerPool.get(info, true,
4271
 
                                                    MERGE_READ_BUFFER_SIZE,
4272
 
                                                    -1);
4273
 
        merge.readers.add(reader);
4274
 
 
4275
 
        // We clone the segment readers because other
4276
 
        // deletes may come in while we're merging so we
4277
 
        // need readers that will not change
4278
 
        final SegmentReader clone = (SegmentReader) reader.clone(true);
4279
 
        merge.readerClones.add(clone);
4280
 
 
4281
 
        if (clone.numDocs() > 0) {
4282
 
          merger.add(clone);
4283
 
          totDocCount += clone.numDocs();
4284
 
        }
4285
 
        segUpto++;
4286
 
      }
4287
 
 
4288
 
      if (infoStream != null) {
4289
 
        message("merge: total " + totDocCount + " docs");
4290
 
      }
4291
 
 
4292
 
      merge.checkAborted(directory);
4293
 
 
4294
 
      // This is where all the work happens:
4295
 
      mergedDocCount = merge.info.docCount = merger.merge();
4296
 
 
4297
 
      // LUCENE-3403: set hasVectors after merge(), so that it is properly set.
4298
 
      merge.info.setHasVectors(merger.fieldInfos().hasVectors());
4299
 
 
4300
 
      assert mergedDocCount == totDocCount;
4301
 
 
4302
 
      if (infoStream != null) {
4303
 
        message("merge store matchedCount=" + merger.getMatchedSubReaderCount() + " vs " + merge.readers.size());
4304
 
      }
4305
 
 
4306
 
      anyNonBulkMerges |= merger.getAnyNonBulkMerges();
4307
 
      
4308
 
      assert mergedDocCount == totDocCount: "mergedDocCount=" + mergedDocCount + " vs " + totDocCount;
4309
 
 
4310
 
      // Very important to do this before opening the reader
4311
 
      // because SegmentReader must know if prox was written for
4312
 
      // this segment:
4313
 
      merge.info.setHasProx(merger.fieldInfos().hasProx());
4314
 
 
4315
 
      boolean useCompoundFile;
4316
 
      synchronized (this) { // Guard segmentInfos
4317
 
        useCompoundFile = mergePolicy.useCompoundFile(segmentInfos, merge.info);
4318
 
      }
4319
 
 
4320
 
      if (useCompoundFile) {
4321
 
 
4322
 
        success = false;
4323
 
        final String compoundFileName = IndexFileNames.segmentFileName(mergedName, IndexFileNames.COMPOUND_FILE_EXTENSION);
4324
 
 
4325
 
        try {
4326
 
          if (infoStream != null) {
4327
 
            message("create compound file " + compoundFileName);
4328
 
          }
4329
 
          merger.createCompoundFile(compoundFileName, merge.info);
4330
 
          success = true;
4331
 
        } catch (IOException ioe) {
4332
 
          synchronized(this) {
4333
 
            if (merge.isAborted()) {
4334
 
              // This can happen if rollback or close(false)
4335
 
              // is called -- fall through to logic below to
4336
 
              // remove the partially created CFS:
4337
 
            } else {
4338
 
              handleMergeException(ioe, merge);
4339
 
            }
4340
 
          }
4341
 
        } catch (Throwable t) {
4342
 
          handleMergeException(t, merge);
4343
 
        } finally {
4344
 
          if (!success) {
4345
 
            if (infoStream != null) {
4346
 
              message("hit exception creating compound file during merge");
4347
 
            }
4348
 
 
4349
 
            synchronized(this) {
4350
 
              deleter.deleteFile(compoundFileName);
4351
 
              deleter.deleteNewFiles(merge.info.files());
4352
 
            }
4353
 
          }
4354
 
        }
4355
 
 
4356
 
        success = false;
4357
 
 
4358
 
        synchronized(this) {
4359
 
 
4360
 
          // delete new non cfs files directly: they were never
4361
 
          // registered with IFD
4362
 
          deleter.deleteNewFiles(merge.info.files());
4363
 
 
4364
 
          if (merge.isAborted()) {
4365
 
            if (infoStream != null) {
4366
 
              message("abort merge after building CFS");
4367
 
            }
4368
 
            deleter.deleteFile(compoundFileName);
4369
 
            return 0;
4370
 
          }
4371
 
        }
4372
 
 
4373
 
        merge.info.setUseCompoundFile(true);
4374
 
      }
4375
 
 
4376
 
      if (infoStream != null) {
4377
 
        message(String.format("merged segment size=%.3f MB vs estimate=%.3f MB", merge.info.sizeInBytes(true)/1024./1024., merge.estimatedMergeBytes/1024/1024.));
4378
 
      }
4379
 
 
4380
 
      final IndexReaderWarmer mergedSegmentWarmer = config.getMergedSegmentWarmer();
4381
 
 
4382
 
      final int termsIndexDivisor;
4383
 
      final boolean loadDocStores;
4384
 
 
4385
 
      if (mergedSegmentWarmer != null) {
4386
 
        // Load terms index & doc stores so the segment
4387
 
        // warmer can run searches, load documents/term
4388
 
        // vectors
4389
 
        termsIndexDivisor = config.getReaderTermsIndexDivisor();
4390
 
        loadDocStores = true;
4391
 
      } else {
4392
 
        termsIndexDivisor = -1;
4393
 
        loadDocStores = false;
4394
 
      }
4395
 
 
4396
 
      // TODO: in the non-realtime case, we may want to only
4397
 
      // keep deletes (it's costly to open entire reader
4398
 
      // when we just need deletes)
4399
 
 
4400
 
      final SegmentReader mergedReader = readerPool.get(merge.info, loadDocStores, BufferedIndexInput.BUFFER_SIZE, termsIndexDivisor);
4401
 
      try {
4402
 
        if (poolReaders && mergedSegmentWarmer != null) {
4403
 
          mergedSegmentWarmer.warm(mergedReader);
4404
 
        }
4405
 
 
4406
 
        if (!commitMerge(merge, mergedReader)) {
4407
 
          // commitMerge will return false if this merge was aborted
4408
 
          return 0;
4409
 
        }
4410
 
      } finally {
4411
 
        synchronized(this) {
4412
 
          if (readerPool.release(mergedReader)) {
4413
 
            // Must checkpoint after releasing the
4414
 
            // mergedReader since it may have written a new
4415
 
            // deletes file:
4416
 
            checkpoint();
4417
 
          }
4418
 
        }
4419
 
      }
4420
 
 
4421
 
      success = true;
4422
 
 
4423
 
    } finally {
4424
 
      // Readers are already closed in commitMerge if we didn't hit
4425
 
      // an exc:
4426
 
      if (!success) {
4427
 
        closeMergeReaders(merge, true);
4428
 
      }
4429
 
    }
4430
 
 
4431
 
    return mergedDocCount;
4432
 
  }
4433
 
 
4434
 
  synchronized void addMergeException(MergePolicy.OneMerge merge) {
4435
 
    assert merge.getException() != null;
4436
 
    if (!mergeExceptions.contains(merge) && mergeGen == merge.mergeGen)
4437
 
      mergeExceptions.add(merge);
4438
 
  }
4439
 
 
4440
 
  // For test purposes.
4441
 
  final int getBufferedDeleteTermsSize() {
4442
 
    return docWriter.getPendingDeletes().terms.size();
4443
 
  }
4444
 
 
4445
 
  // For test purposes.
4446
 
  final int getNumBufferedDeleteTerms() {
4447
 
    return docWriter.getPendingDeletes().numTermDeletes.get();
4448
 
  }
4449
 
 
4450
 
  // utility routines for tests
4451
 
  synchronized SegmentInfo newestSegment() {
4452
 
    return segmentInfos.size() > 0 ? segmentInfos.info(segmentInfos.size()-1) : null;
4453
 
  }
4454
 
 
4455
 
  /** @lucene.internal */
4456
 
  public synchronized String segString() throws IOException {
4457
 
    return segString(segmentInfos);
4458
 
  }
4459
 
 
4460
 
  /** @lucene.internal */
4461
 
  public synchronized String segString(Iterable<SegmentInfo> infos) throws IOException {
4462
 
    final StringBuilder buffer = new StringBuilder();
4463
 
    for(final SegmentInfo s : infos) {
4464
 
      if (buffer.length() > 0) {
4465
 
        buffer.append(' ');
4466
 
      }
4467
 
      buffer.append(segString(s));
4468
 
    }
4469
 
    return buffer.toString();
4470
 
  }
4471
 
 
4472
 
  /** @lucene.internal */
4473
 
  public synchronized String segString(SegmentInfo info) throws IOException {
4474
 
    StringBuilder buffer = new StringBuilder();
4475
 
    SegmentReader reader = readerPool.getIfExists(info);
4476
 
    try {
4477
 
      if (reader != null) {
4478
 
        buffer.append(reader.toString());
4479
 
      } else {
4480
 
        buffer.append(info.toString(directory, 0));
4481
 
        if (info.dir != directory) {
4482
 
          buffer.append("**");
4483
 
        }
4484
 
      }
4485
 
    } finally {
4486
 
      if (reader != null) {
4487
 
        readerPool.release(reader);
4488
 
      }
4489
 
    }
4490
 
    return buffer.toString();
4491
 
  }
4492
 
 
4493
 
  private synchronized void doWait() {
4494
 
    // NOTE: the callers of this method should in theory
4495
 
    // be able to do simply wait(), but, as a defense
4496
 
    // against thread timing hazards where notifyAll()
4497
 
    // fails to be called, we wait for at most 1 second
4498
 
    // and then return so caller can check if wait
4499
 
    // conditions are satisfied:
4500
 
    try {
4501
 
      wait(1000);
4502
 
    } catch (InterruptedException ie) {
4503
 
      throw new ThreadInterruptedException(ie);
4504
 
    }
4505
 
  }
4506
 
 
4507
 
  private boolean keepFullyDeletedSegments;
4508
 
 
4509
 
  /** Only for testing.
4510
 
   *
4511
 
   * @lucene.internal */
4512
 
  void keepFullyDeletedSegments() {
4513
 
    keepFullyDeletedSegments = true;
4514
 
  }
4515
 
 
4516
 
  boolean getKeepFullyDeletedSegments() {
4517
 
    return keepFullyDeletedSegments;
4518
 
  }
4519
 
 
4520
 
  // called only from assert
4521
 
  private boolean filesExist(SegmentInfos toSync) throws IOException {
4522
 
    Collection<String> files = toSync.files(directory, false);
4523
 
    for(final String fileName: files) {
4524
 
      assert directory.fileExists(fileName): "file " + fileName + " does not exist";
4525
 
      // If this trips it means we are missing a call to
4526
 
      // .checkpoint somewhere, because by the time we
4527
 
      // are called, deleter should know about every
4528
 
      // file referenced by the current head
4529
 
      // segmentInfos:
4530
 
      assert deleter.exists(fileName): "IndexFileDeleter doesn't know about file " + fileName;
4531
 
    }
4532
 
    return true;
4533
 
  }
4534
 
 
4535
 
  /** Walk through all files referenced by the current
4536
 
   *  segmentInfos and ask the Directory to sync each file,
4537
 
   *  if it wasn't already.  If that succeeds, then we
4538
 
   *  prepare a new segments_N file but do not fully commit
4539
 
   *  it. */
4540
 
  private void startCommit(SegmentInfos toSync, Map<String,String> commitUserData) throws IOException {
4541
 
 
4542
 
    assert testPoint("startStartCommit");
4543
 
    assert pendingCommit == null;
4544
 
 
4545
 
    if (hitOOM) {
4546
 
      throw new IllegalStateException("this writer hit an OutOfMemoryError; cannot commit");
4547
 
    }
4548
 
 
4549
 
    try {
4550
 
 
4551
 
      if (infoStream != null)
4552
 
        message("startCommit(): start");
4553
 
 
4554
 
 
4555
 
      synchronized(this) {
4556
 
 
4557
 
        assert lastCommitChangeCount <= changeCount;
4558
 
        
4559
 
        if (pendingCommitChangeCount == lastCommitChangeCount) {
4560
 
          if (infoStream != null) {
4561
 
            message("  skip startCommit(): no changes pending");
4562
 
          }
4563
 
          deleter.decRef(toSync);
4564
 
          return;
4565
 
        }
4566
 
        
4567
 
        // First, we clone & incref the segmentInfos we intend
4568
 
        // to sync, then, without locking, we sync() all files
4569
 
        // referenced by toSync, in the background.
4570
 
        
4571
 
        if (infoStream != null)
4572
 
          message("startCommit index=" + segString(toSync) + " changeCount=" + changeCount);
4573
 
 
4574
 
        assert filesExist(toSync);
4575
 
        
4576
 
        if (commitUserData != null) {
4577
 
          toSync.setUserData(commitUserData);
4578
 
        }
4579
 
      }
4580
 
 
4581
 
      assert testPoint("midStartCommit");
4582
 
 
4583
 
      boolean pendingCommitSet = false;
4584
 
 
4585
 
      try {
4586
 
        // This call can take a long time -- 10s of seconds
4587
 
        // or more.  We do it without sync:
4588
 
        directory.sync(toSync.files(directory, false));
4589
 
 
4590
 
        assert testPoint("midStartCommit2");
4591
 
 
4592
 
        synchronized(this) {
4593
 
 
4594
 
          assert pendingCommit == null;
4595
 
 
4596
 
          assert segmentInfos.getGeneration() == toSync.getGeneration();
4597
 
 
4598
 
          // Exception here means nothing is prepared
4599
 
          // (this method unwinds everything it did on
4600
 
          // an exception)
4601
 
          toSync.prepareCommit(directory);
4602
 
          pendingCommitSet = true;
4603
 
          pendingCommit = toSync;
4604
 
        }
4605
 
 
4606
 
        if (infoStream != null) {
4607
 
          message("done all syncs");
4608
 
        }
4609
 
 
4610
 
        assert testPoint("midStartCommitSuccess");
4611
 
 
4612
 
      } finally {
4613
 
        synchronized(this) {
4614
 
 
4615
 
          // Have our master segmentInfos record the
4616
 
          // generations we just prepared.  We do this
4617
 
          // on error or success so we don't
4618
 
          // double-write a segments_N file.
4619
 
          segmentInfos.updateGeneration(toSync);
4620
 
 
4621
 
          if (!pendingCommitSet) {
4622
 
            if (infoStream != null) {
4623
 
              message("hit exception committing segments file");
4624
 
            }
4625
 
 
4626
 
            deleter.decRef(toSync);
4627
 
          }
4628
 
        }
4629
 
      }
4630
 
    } catch (OutOfMemoryError oom) {
4631
 
      handleOOM(oom, "startCommit");
4632
 
    }
4633
 
    assert testPoint("finishStartCommit");
4634
 
  }
4635
 
 
4636
 
  /**
4637
 
   * Returns <code>true</code> iff the index in the named directory is
4638
 
   * currently locked.
4639
 
   * @param directory the directory to check for a lock
4640
 
   * @throws IOException if there is a low-level IO error
4641
 
   */
4642
 
  public static boolean isLocked(Directory directory) throws IOException {
4643
 
    return directory.makeLock(WRITE_LOCK_NAME).isLocked();
4644
 
  }
4645
 
 
4646
 
  /**
4647
 
   * Forcibly unlocks the index in the named directory.
4648
 
   * <P>
4649
 
   * Caution: this should only be used by failure recovery code,
4650
 
   * when it is known that no other process nor thread is in fact
4651
 
   * currently accessing this index.
4652
 
   */
4653
 
  public static void unlock(Directory directory) throws IOException {
4654
 
    directory.makeLock(IndexWriter.WRITE_LOCK_NAME).release();
4655
 
  }
4656
 
 
4657
 
  /**
4658
 
   * Specifies maximum field length (in number of tokens/terms) in
4659
 
   * {@link IndexWriter} constructors. {@link #setMaxFieldLength(int)} overrides
4660
 
   * the value set by the constructor.
4661
 
   * 
4662
 
   * @deprecated use {@link LimitTokenCountAnalyzer} instead.
4663
 
   */
4664
 
  @Deprecated
4665
 
  public static final class MaxFieldLength {
4666
 
 
4667
 
    private int limit;
4668
 
    private String name;
4669
 
 
4670
 
    /**
4671
 
     * Private type-safe-enum-pattern constructor.
4672
 
     * 
4673
 
     * @param name instance name
4674
 
     * @param limit maximum field length
4675
 
     */
4676
 
    private MaxFieldLength(String name, int limit) {
4677
 
      this.name = name;
4678
 
      this.limit = limit;
4679
 
    }
4680
 
 
4681
 
    /**
4682
 
     * Public constructor to allow users to specify the maximum field size limit.
4683
 
     * 
4684
 
     * @param limit The maximum field length
4685
 
     */
4686
 
    public MaxFieldLength(int limit) {
4687
 
      this("User-specified", limit);
4688
 
    }
4689
 
    
4690
 
    public int getLimit() {
4691
 
      return limit;
4692
 
    }
4693
 
    
4694
 
    @Override
4695
 
    public String toString()
4696
 
    {
4697
 
      return name + ":" + limit;
4698
 
    }
4699
 
 
4700
 
    /** Sets the maximum field length to {@link Integer#MAX_VALUE}. */
4701
 
    public static final MaxFieldLength UNLIMITED
4702
 
        = new MaxFieldLength("UNLIMITED", Integer.MAX_VALUE);
4703
 
 
4704
 
    /**
4705
 
     *  Sets the maximum field length to 
4706
 
     * {@link #DEFAULT_MAX_FIELD_LENGTH} 
4707
 
     * */
4708
 
    public static final MaxFieldLength LIMITED
4709
 
        = new MaxFieldLength("LIMITED", 10000);
4710
 
  }
4711
 
 
4712
 
  /** If {@link #getReader} has been called (ie, this writer
4713
 
   *  is in near real-time mode), then after a merge
4714
 
   *  completes, this class can be invoked to warm the
4715
 
   *  reader on the newly merged segment, before the merge
4716
 
   *  commits.  This is not required for near real-time
4717
 
   *  search, but will reduce search latency on opening a
4718
 
   *  new near real-time reader after a merge completes.
4719
 
   *
4720
 
   * @lucene.experimental
4721
 
   *
4722
 
   * <p><b>NOTE</b>: warm is called before any deletes have
4723
 
   * been carried over to the merged segment. */
4724
 
  public static abstract class IndexReaderWarmer {
4725
 
    public abstract void warm(IndexReader reader) throws IOException;
4726
 
  }
4727
 
 
4728
 
  /**
4729
 
   * Set the merged segment warmer. See {@link IndexReaderWarmer}.
4730
 
   * 
4731
 
   * @deprecated use
4732
 
   *             {@link IndexWriterConfig#setMergedSegmentWarmer}
4733
 
   *             instead.
4734
 
   */
4735
 
  @Deprecated
4736
 
  public void setMergedSegmentWarmer(IndexReaderWarmer warmer) {
4737
 
    config.setMergedSegmentWarmer(warmer);
4738
 
  }
4739
 
 
4740
 
  /**
4741
 
   * Returns the current merged segment warmer. See {@link IndexReaderWarmer}.
4742
 
   * 
4743
 
   * @deprecated use {@link IndexWriterConfig#getMergedSegmentWarmer()} instead.
4744
 
   */
4745
 
  @Deprecated
4746
 
  public IndexReaderWarmer getMergedSegmentWarmer() {
4747
 
    return config.getMergedSegmentWarmer();
4748
 
  }
4749
 
 
4750
 
  private void handleOOM(OutOfMemoryError oom, String location) {
4751
 
    if (infoStream != null) {
4752
 
      message("hit OutOfMemoryError inside " + location);
4753
 
    }
4754
 
    hitOOM = true;
4755
 
    throw oom;
4756
 
  }
4757
 
 
4758
 
  // Used only by assert for testing.  Current points:
4759
 
  //   startDoFlush
4760
 
  //   startCommitMerge
4761
 
  //   startStartCommit
4762
 
  //   midStartCommit
4763
 
  //   midStartCommit2
4764
 
  //   midStartCommitSuccess
4765
 
  //   finishStartCommit
4766
 
  //   startCommitMergeDeletes
4767
 
  //   startMergeInit
4768
 
  //   DocumentsWriter.ThreadState.init start
4769
 
  boolean testPoint(String name) {
4770
 
    return true;
4771
 
  }
4772
 
 
4773
 
  synchronized boolean nrtIsCurrent(SegmentInfos infos) {
4774
 
    //System.out.println("IW.nrtIsCurrent " + (infos.version == segmentInfos.version && !docWriter.anyChanges() && !bufferedDeletesStream.any()));
4775
 
    ensureOpen();
4776
 
    return infos.version == segmentInfos.version && !docWriter.anyChanges() && !bufferedDeletesStream.any();
4777
 
  }
4778
 
 
4779
 
  synchronized boolean isClosed() {
4780
 
    return closed;
4781
 
  }
4782
 
 
4783
 
  /** Expert: remove any index files that are no longer
4784
 
   *  used.
4785
 
   * 
4786
 
   *  <p> IndexWriter normally deletes unused files itself,
4787
 
   *  during indexing.  However, on Windows, which disallows
4788
 
   *  deletion of open files, if there is a reader open on
4789
 
   *  the index then those files cannot be deleted.  This is
4790
 
   *  fine, because IndexWriter will periodically retry
4791
 
   *  the deletion.</p>
4792
 
   *
4793
 
   *  <p> However, IndexWriter doesn't try that often: only
4794
 
   *  on open, close, flushing a new segment, and finishing
4795
 
   *  a merge.  If you don't do any of these actions with your
4796
 
   *  IndexWriter, you'll see the unused files linger.  If
4797
 
   *  that's a problem, call this method to delete them
4798
 
   *  (once you've closed the open readers that were
4799
 
   *  preventing their deletion). 
4800
 
   *  
4801
 
   *  <p> In addition, you can call this method to delete 
4802
 
   *  unreferenced index commits. This might be useful if you 
4803
 
   *  are using an {@link IndexDeletionPolicy} which holds
4804
 
   *  onto index commits until some criteria are met, but those
4805
 
   *  commits are no longer needed. Otherwise, those commits will
4806
 
   *  be deleted the next time commit() is called.
4807
 
   */
4808
 
  public synchronized void deleteUnusedFiles() throws IOException {
4809
 
    ensureOpen(false);
4810
 
    deleter.deletePendingFiles();
4811
 
    deleter.revisitPolicy();
4812
 
  }
4813
 
 
4814
 
  // Called by DirectoryReader.doClose
4815
 
  synchronized void deletePendingFiles() throws IOException {
4816
 
    deleter.deletePendingFiles();
4817
 
  }
4818
 
 
4819
 
  /**
4820
 
   * Sets the {@link PayloadProcessorProvider} to use when merging payloads.
4821
 
   * Note that the given <code>pcp</code> will be invoked for every segment that
4822
 
   * is merged, not only external ones that are given through
4823
 
   * {@link #addIndexes}. If you want only the payloads of the external segments
4824
 
   * to be processed, you can return <code>null</code> whenever a
4825
 
   * {@link DirPayloadProcessor} is requested for the {@link Directory} of the
4826
 
   * {@link IndexWriter}.
4827
 
   * <p>
4828
 
   * The default is <code>null</code> which means payloads are processed
4829
 
   * normally (copied) during segment merges. You can also unset it by passing
4830
 
   * <code>null</code>.
4831
 
   * <p>
4832
 
   * <b>NOTE:</b> the set {@link PayloadProcessorProvider} will be in effect
4833
 
   * immediately, potentially for already running merges too. If you want to be
4834
 
   * sure it is used for further operations only, such as {@link #addIndexes} or
4835
 
   * {@link #forceMerge}, you can call {@link #waitForMerges()} before.
4836
 
   */
4837
 
  public void setPayloadProcessorProvider(PayloadProcessorProvider pcp) {
4838
 
    ensureOpen();
4839
 
    payloadProcessorProvider = pcp;
4840
 
  }
4841
 
  
4842
 
  /**
4843
 
   * Returns the {@link PayloadProcessorProvider} that is used during segment
4844
 
   * merges to process payloads.
4845
 
   */
4846
 
  public PayloadProcessorProvider getPayloadProcessorProvider() {
4847
 
    ensureOpen();
4848
 
    return payloadProcessorProvider;
4849
 
  }
4850
 
 
4851
 
  // decides when flushes happen
4852
 
  final class FlushControl {
4853
 
 
4854
 
    private boolean flushPending;
4855
 
    private boolean flushDeletes;
4856
 
    private int delCount;
4857
 
    private int docCount;
4858
 
    private boolean flushing;
4859
 
 
4860
 
    private synchronized boolean setFlushPending(String reason, boolean doWait) {
4861
 
      if (flushPending || flushing) {
4862
 
        if (doWait) {
4863
 
          while(flushPending || flushing) {
4864
 
            try {
4865
 
              wait();
4866
 
            } catch (InterruptedException ie) {
4867
 
              throw new ThreadInterruptedException(ie);
4868
 
            }
4869
 
          }
4870
 
        }
4871
 
        return false;
4872
 
      } else {
4873
 
        if (infoStream != null) {
4874
 
          message("now trigger flush reason=" + reason);
4875
 
        }
4876
 
        flushPending = true;
4877
 
        return flushPending;
4878
 
      }
4879
 
    }
4880
 
 
4881
 
    public synchronized void setFlushPendingNoWait(String reason) {
4882
 
      setFlushPending(reason, false);
4883
 
    }
4884
 
 
4885
 
    public synchronized boolean getFlushPending() {
4886
 
      return flushPending;
4887
 
    }
4888
 
 
4889
 
    public synchronized boolean getFlushDeletes() {
4890
 
      return flushDeletes;
4891
 
    }
4892
 
 
4893
 
    public synchronized void clearFlushPending() {
4894
 
      if (infoStream != null) {
4895
 
        message("clearFlushPending");
4896
 
      }
4897
 
      flushPending = false;
4898
 
      flushDeletes = false;
4899
 
      docCount = 0;
4900
 
      notifyAll();
4901
 
    }
4902
 
 
4903
 
    public synchronized void clearDeletes() {
4904
 
      delCount = 0;
4905
 
    }
4906
 
 
4907
 
    public synchronized boolean waitUpdate(int docInc, int delInc) {
4908
 
      return waitUpdate(docInc, delInc, false);
4909
 
    }
4910
 
 
4911
 
    public synchronized boolean waitUpdate(int docInc, int delInc, boolean skipWait) {
4912
 
      while(flushPending) {
4913
 
        try {
4914
 
          wait();
4915
 
        } catch (InterruptedException ie) {
4916
 
          throw new ThreadInterruptedException(ie);
4917
 
        }
4918
 
      }
4919
 
 
4920
 
      docCount += docInc;
4921
 
      delCount += delInc;
4922
 
 
4923
 
      // skipWait is only used when a thread is BOTH adding
4924
 
      // a doc and buffering a del term, and, the adding of
4925
 
      // the doc already triggered a flush
4926
 
      if (skipWait) {
4927
 
        return false;
4928
 
      }
4929
 
 
4930
 
      final int maxBufferedDocs = config.getMaxBufferedDocs();
4931
 
      if (maxBufferedDocs != IndexWriterConfig.DISABLE_AUTO_FLUSH &&
4932
 
          docCount >= maxBufferedDocs) {
4933
 
        return setFlushPending("maxBufferedDocs", true);
4934
 
      }
4935
 
 
4936
 
      final int maxBufferedDeleteTerms = config.getMaxBufferedDeleteTerms();
4937
 
      if (maxBufferedDeleteTerms != IndexWriterConfig.DISABLE_AUTO_FLUSH &&
4938
 
          delCount >= maxBufferedDeleteTerms) {
4939
 
        flushDeletes = true;
4940
 
        return setFlushPending("maxBufferedDeleteTerms", true);
4941
 
      }
4942
 
 
4943
 
      return flushByRAMUsage("add delete/doc");
4944
 
    }
4945
 
 
4946
 
    public synchronized boolean flushByRAMUsage(String reason) {
4947
 
      final double ramBufferSizeMB = config.getRAMBufferSizeMB();
4948
 
      if (ramBufferSizeMB != IndexWriterConfig.DISABLE_AUTO_FLUSH) {
4949
 
        final long limit = (long) (ramBufferSizeMB*1024*1024);
4950
 
        long used = bufferedDeletesStream.bytesUsed() + docWriter.bytesUsed();
4951
 
        if (used >= limit) {
4952
 
          
4953
 
          // DocumentsWriter may be able to free up some
4954
 
          // RAM:
4955
 
          // Lock order: FC -> DW
4956
 
          docWriter.balanceRAM();
4957
 
 
4958
 
          used = bufferedDeletesStream.bytesUsed() + docWriter.bytesUsed();
4959
 
          if (used >= limit) {
4960
 
            return setFlushPending("ram full: " + reason, false);
4961
 
          }
4962
 
        }
4963
 
      }
4964
 
      return false;
4965
 
    }
4966
 
  }
4967
 
 
4968
 
  final FlushControl flushControl = new FlushControl();
4969
 
}