View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.index.context;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.nio.channels.FileChannel;
24  import java.nio.channels.FileLock;
25  import java.nio.file.Files;
26  import java.nio.file.Path;
27  import java.nio.file.StandardOpenOption;
28  import java.util.Collection;
29  import java.util.Collections;
30  import java.util.Date;
31  import java.util.HashSet;
32  import java.util.LinkedHashSet;
33  import java.util.List;
34  import java.util.Set;
35  import java.util.concurrent.atomic.AtomicReference;
36  
37  import org.apache.lucene.analysis.Analyzer;
38  import org.apache.lucene.document.Document;
39  import org.apache.lucene.document.Field;
40  import org.apache.lucene.document.StoredField;
41  import org.apache.lucene.index.CorruptIndexException;
42  import org.apache.lucene.index.DirectoryReader;
43  import org.apache.lucene.index.IndexReader;
44  import org.apache.lucene.index.IndexWriter;
45  import org.apache.lucene.index.IndexWriterConfig;
46  import org.apache.lucene.index.MultiBits;
47  import org.apache.lucene.index.StoredFields;
48  import org.apache.lucene.index.Term;
49  import org.apache.lucene.search.IndexSearcher;
50  import org.apache.lucene.search.SearcherManager;
51  import org.apache.lucene.search.TermQuery;
52  import org.apache.lucene.search.TopScoreDocCollector;
53  import org.apache.lucene.store.Directory;
54  import org.apache.lucene.store.FSDirectory;
55  import org.apache.lucene.store.FSLockFactory;
56  import org.apache.lucene.store.Lock;
57  import org.apache.lucene.store.LockObtainFailedException;
58  import org.apache.lucene.util.Bits;
59  import org.apache.maven.index.ArtifactInfo;
60  import org.apache.maven.index.IndexerField;
61  import org.apache.maven.index.artifact.GavCalculator;
62  import org.apache.maven.index.artifact.M2GavCalculator;
63  import org.codehaus.plexus.util.StringUtils;
64  
65  /**
66   * The default {@link IndexingContext} implementation.
67   *
68   * @author Jason van Zyl
69   * @author Tamas Cservenak
70   */
71  public class DefaultIndexingContext extends AbstractIndexingContext {
72      /**
73       * A standard location for indices served up by a webserver.
74       */
75      private static final String INDEX_DIRECTORY = ".index";
76  
77      public static final String FLD_DESCRIPTOR = "DESCRIPTOR";
78  
79      public static final String FLD_DESCRIPTOR_CONTENTS = "NexusIndex";
80  
81      public static final String FLD_IDXINFO = "IDXINFO";
82  
83      public static final String VERSION = "1.0";
84  
85      private static final Term DESCRIPTOR_TERM = new Term(FLD_DESCRIPTOR, FLD_DESCRIPTOR_CONTENTS);
86  
87      private Directory indexDirectory;
88  
89      private TrackingLockFactory lockFactory;
90  
91      private File indexDirectoryFile;
92  
93      private String id;
94  
95      private boolean searchable;
96  
97      private String repositoryId;
98  
99      private File repository;
100 
101     private String repositoryUrl;
102 
103     private String indexUpdateUrl;
104 
105     private NexusIndexWriter indexWriter;
106 
107     private SearcherManager searcherManager;
108 
109     private Date timestamp;
110 
111     private List<? extends IndexCreator> indexCreators;
112 
113     /**
114      * Currently nexus-indexer knows only M2 reposes
115      * <p>
116      * XXX move this into a concrete Scanner implementation
117      */
118     private GavCalculator gavCalculator;
119 
120     private DefaultIndexingContext(
121             String id,
122             String repositoryId,
123             File repository, //
124             String repositoryUrl,
125             String indexUpdateUrl,
126             List<? extends IndexCreator> indexCreators,
127             Directory indexDirectory,
128             TrackingLockFactory lockFactory,
129             boolean reclaimIndex,
130             File indexDirectoryFile)
131             throws ExistingLuceneIndexMismatchException, IOException {
132 
133         this.id = id;
134 
135         this.searchable = true;
136 
137         this.repositoryId = repositoryId;
138 
139         this.repository = repository;
140 
141         this.repositoryUrl = repositoryUrl;
142 
143         this.indexUpdateUrl = indexUpdateUrl;
144 
145         this.indexWriter = null;
146 
147         this.searcherManager = null;
148 
149         this.indexCreators = indexCreators;
150 
151         this.indexDirectory = indexDirectory;
152 
153         this.lockFactory = lockFactory;
154 
155         // eh?
156         // Guice does NOT initialize these, and we have to do manually?
157         // While in Plexus, all is well, but when in guice-shim,
158         // these objects are still LazyHintedBeans or what not and IndexerFields are NOT registered!
159         for (IndexCreator indexCreator : indexCreators) {
160             indexCreator.getIndexerFields();
161         }
162 
163         this.gavCalculator = new M2GavCalculator();
164 
165         prepareIndex(reclaimIndex);
166 
167         setIndexDirectoryFile(indexDirectoryFile);
168     }
169 
170     private DefaultIndexingContext(
171             String id,
172             String repositoryId,
173             File repository,
174             File indexDirectoryFile,
175             TrackingLockFactory lockFactory,
176             String repositoryUrl,
177             String indexUpdateUrl,
178             List<? extends IndexCreator> indexCreators,
179             boolean reclaimIndex)
180             throws IOException, ExistingLuceneIndexMismatchException {
181         this(
182                 id,
183                 repositoryId,
184                 repository,
185                 repositoryUrl,
186                 indexUpdateUrl,
187                 indexCreators,
188                 FSDirectory.open(indexDirectoryFile.toPath(), lockFactory),
189                 lockFactory,
190                 reclaimIndex,
191                 indexDirectoryFile);
192     }
193 
194     public DefaultIndexingContext(
195             String id,
196             String repositoryId,
197             File repository,
198             File indexDirectoryFile,
199             String repositoryUrl,
200             String indexUpdateUrl,
201             List<? extends IndexCreator> indexCreators,
202             boolean reclaimIndex)
203             throws IOException, ExistingLuceneIndexMismatchException {
204         this(
205                 id,
206                 repositoryId,
207                 repository,
208                 indexDirectoryFile,
209                 new TrackingLockFactory(FSLockFactory.getDefault()),
210                 repositoryUrl,
211                 indexUpdateUrl,
212                 indexCreators,
213                 reclaimIndex);
214     }
215 
216     @Deprecated
217     public DefaultIndexingContext(
218             String id,
219             String repositoryId,
220             File repository,
221             Directory indexDirectory,
222             String repositoryUrl,
223             String indexUpdateUrl,
224             List<? extends IndexCreator> indexCreators,
225             boolean reclaimIndex)
226             throws IOException, ExistingLuceneIndexMismatchException {
227         this(
228                 id,
229                 repositoryId,
230                 repository,
231                 repositoryUrl,
232                 indexUpdateUrl,
233                 indexCreators,
234                 indexDirectory,
235                 null,
236                 reclaimIndex,
237                 indexDirectory instanceof FSDirectory
238                         ? ((FSDirectory) indexDirectory).getDirectory().toFile()
239                         : null); // Lock factory already installed - pass null
240     }
241 
242     public Directory getIndexDirectory() {
243         return indexDirectory;
244     }
245 
246     /**
247      * Sets index location. As usually index is persistent (is on disk), this will point to that value, but in
248      * some circumstances (ie, using RAMDisk for index), this will point to an existing tmp directory.
249      */
250     protected void setIndexDirectoryFile(File dir) throws IOException {
251         if (dir == null) {
252             // best effort, to have a directory through the life of a ctx
253             this.indexDirectoryFile =
254                     Files.createTempDirectory("mindexer-ctx" + id).toFile();
255             this.indexDirectoryFile.deleteOnExit();
256         } else {
257             this.indexDirectoryFile = dir;
258         }
259     }
260 
261     public File getIndexDirectoryFile() {
262         return indexDirectoryFile;
263     }
264 
265     private void prepareIndex(boolean reclaimIndex) throws IOException, ExistingLuceneIndexMismatchException {
266         if (DirectoryReader.indexExists(indexDirectory)) {
267             try {
268                 // unlock the dir forcibly
269                 try {
270                     indexDirectory.obtainLock(IndexWriter.WRITE_LOCK_NAME).close();
271                 } catch (LockObtainFailedException failed) {
272                     unlockForcibly(lockFactory, indexDirectory);
273                 }
274 
275                 openAndWarmup();
276 
277                 checkAndUpdateIndexDescriptor(reclaimIndex);
278             } catch (IOException e) {
279                 if (reclaimIndex) {
280                     prepareCleanIndex(true);
281                 } else {
282                     throw e;
283                 }
284             }
285         } else {
286             prepareCleanIndex(false);
287         }
288 
289         timestamp = IndexUtils.getTimestamp(indexDirectory);
290     }
291 
292     private void prepareCleanIndex(boolean deleteExisting) throws IOException {
293         if (deleteExisting) {
294             closeReaders();
295 
296             // unlock the dir forcibly
297             try {
298                 indexDirectory.obtainLock(IndexWriter.WRITE_LOCK_NAME).close();
299             } catch (LockObtainFailedException failed) {
300                 unlockForcibly(lockFactory, indexDirectory);
301             }
302 
303             deleteIndexFiles(true);
304         }
305 
306         openAndWarmup();
307 
308         if (StringUtils.isEmpty(getRepositoryId())) {
309             throw new IllegalArgumentException("The repositoryId cannot be null when creating new repository!");
310         }
311 
312         storeDescriptor();
313     }
314 
315     private void checkAndUpdateIndexDescriptor(boolean reclaimIndex)
316             throws IOException, ExistingLuceneIndexMismatchException {
317         if (reclaimIndex) {
318             // forcefully "reclaiming" the ownership of the index as ours
319             storeDescriptor();
320             return;
321         }
322 
323         // check for descriptor if this is not a "virgin" index
324         if (getSize() > 0) {
325             final TopScoreDocCollector collector = TopScoreDocCollector.create(1, Integer.MAX_VALUE);
326             final IndexSearcher indexSearcher = acquireIndexSearcher();
327             try {
328                 indexSearcher.search(new TermQuery(DESCRIPTOR_TERM), collector);
329 
330                 if (collector.getTotalHits() == 0) {
331                     throw new ExistingLuceneIndexMismatchException("The existing index has no NexusIndexer descriptor");
332                 }
333 
334                 if (collector.getTotalHits() > 1) {
335                     // eh? this is buggy index it seems, just iron it out then
336                     storeDescriptor();
337                 } else {
338                     // good, we have one descriptor as should
339                     Document descriptor = indexSearcher.storedFields().document(collector.topDocs().scoreDocs[0].doc);
340                     String[] h = StringUtils.split(descriptor.get(FLD_IDXINFO), ArtifactInfo.FS);
341                     // String version = h[0];
342                     String repoId = h[1];
343 
344                     // // compare version
345                     // if ( !VERSION.equals( version ) )
346                     // {
347                     // throw new UnsupportedExistingLuceneIndexException(
348                     // "The existing index has version [" + version + "] and not [" + VERSION + "] version!" );
349                     // }
350 
351                     if (getRepositoryId() == null) {
352                         repositoryId = repoId;
353                     } else if (!getRepositoryId().equals(repoId)) {
354                         throw new ExistingLuceneIndexMismatchException(
355                                 "The existing index is for repository " //
356                                         + "[" + repoId + "] and not for repository [" + getRepositoryId() + "]");
357                     }
358                 }
359             } finally {
360                 releaseIndexSearcher(indexSearcher);
361             }
362         }
363     }
364 
365     private void storeDescriptor() throws IOException {
366         Document hdr = new Document();
367 
368         hdr.add(new Field(FLD_DESCRIPTOR, FLD_DESCRIPTOR_CONTENTS, IndexerField.KEYWORD_STORED));
369 
370         hdr.add(new StoredField(
371                 FLD_IDXINFO, VERSION + ArtifactInfo.FS + getRepositoryId(), IndexerField.KEYWORD_STORED));
372 
373         IndexWriter w = getIndexWriter();
374 
375         w.updateDocument(DESCRIPTOR_TERM, hdr);
376 
377         w.commit();
378     }
379 
380     private void deleteIndexFiles(boolean full) throws IOException {
381         if (indexDirectory != null) {
382             String[] names = indexDirectory.listAll();
383 
384             if (names != null) {
385 
386                 for (String name : names) {
387                     if (!(name.equals(INDEX_PACKER_PROPERTIES_FILE) || name.equals(INDEX_UPDATER_PROPERTIES_FILE))) {
388                         indexDirectory.deleteFile(name);
389                     }
390                 }
391             }
392 
393             if (full) {
394                 try {
395                     indexDirectory.deleteFile(INDEX_PACKER_PROPERTIES_FILE);
396                 } catch (IOException ioe) {
397                     // Does not exist
398                 }
399 
400                 try {
401                     indexDirectory.deleteFile(INDEX_UPDATER_PROPERTIES_FILE);
402                 } catch (IOException ioe) {
403                     // Does not exist
404                 }
405             }
406 
407             IndexUtils.deleteTimestamp(indexDirectory);
408         }
409     }
410 
411     // ==
412 
413     public boolean isSearchable() {
414         return searchable;
415     }
416 
417     public void setSearchable(boolean searchable) {
418         this.searchable = searchable;
419     }
420 
421     public String getId() {
422         return id;
423     }
424 
425     public void updateTimestamp() throws IOException {
426         updateTimestamp(false);
427     }
428 
429     public void updateTimestamp(boolean save) throws IOException {
430         updateTimestamp(save, new Date());
431     }
432 
433     public void updateTimestamp(boolean save, Date timestamp) throws IOException {
434         this.timestamp = timestamp;
435 
436         if (save) {
437             IndexUtils.updateTimestamp(indexDirectory, getTimestamp());
438         }
439     }
440 
441     public Date getTimestamp() {
442         return timestamp;
443     }
444 
445     public int getSize() throws IOException {
446         final IndexSearcher is = acquireIndexSearcher();
447         try {
448             return is.getIndexReader().numDocs();
449         } finally {
450             releaseIndexSearcher(is);
451         }
452     }
453 
454     public String getRepositoryId() {
455         return repositoryId;
456     }
457 
458     public File getRepository() {
459         return repository;
460     }
461 
462     public String getRepositoryUrl() {
463         return repositoryUrl;
464     }
465 
466     public String getIndexUpdateUrl() {
467         if (repositoryUrl != null) {
468             if (indexUpdateUrl == null || indexUpdateUrl.isBlank()) {
469                 return repositoryUrl + (repositoryUrl.endsWith("/") ? "" : "/") + INDEX_DIRECTORY;
470             }
471         }
472         return indexUpdateUrl;
473     }
474 
475     public Analyzer getAnalyzer() {
476         return new NexusAnalyzer();
477     }
478 
479     protected void openAndWarmup() throws IOException {
480         // IndexWriter (close)
481         if (indexWriter != null) {
482             indexWriter.close();
483 
484             indexWriter = null;
485         }
486         if (searcherManager != null) {
487             searcherManager.close();
488 
489             searcherManager = null;
490         }
491 
492         this.indexWriter = new NexusIndexWriter(getIndexDirectory(), getWriterConfig());
493         this.indexWriter.commit(); // LUCENE-2386
494         this.searcherManager = new SearcherManager(indexWriter, false, false, new NexusIndexSearcherFactory(this));
495     }
496 
497     /**
498      * Returns new IndexWriterConfig instance
499      *
500      * @since 5.1
501      */
502     protected IndexWriterConfig getWriterConfig() {
503         return NexusIndexWriter.defaultConfig();
504     }
505 
506     public IndexWriter getIndexWriter() throws IOException {
507         return indexWriter;
508     }
509 
510     public IndexSearcher acquireIndexSearcher() throws IOException {
511         // TODO: move this to separate thread to not penalty next incoming searcher
512         searcherManager.maybeRefresh();
513         return searcherManager.acquire();
514     }
515 
516     public void releaseIndexSearcher(final IndexSearcher is) throws IOException {
517         if (is == null) {
518             return;
519         }
520         searcherManager.release(is);
521     }
522 
523     public void commit() throws IOException {
524         getIndexWriter().commit();
525     }
526 
527     public void rollback() throws IOException {
528         getIndexWriter().rollback();
529     }
530 
531     public synchronized void optimize() throws CorruptIndexException, IOException {
532         commit();
533     }
534 
535     public synchronized void close(boolean deleteFiles) throws IOException {
536         if (indexDirectory != null) {
537             IndexUtils.updateTimestamp(indexDirectory, getTimestamp());
538             closeReaders();
539             if (deleteFiles) {
540                 deleteIndexFiles(true);
541             }
542             indexDirectory.close();
543         }
544         indexDirectory = null;
545     }
546 
547     public synchronized void purge() throws IOException {
548         closeReaders();
549         deleteIndexFiles(true);
550         try {
551             prepareIndex(true);
552         } catch (ExistingLuceneIndexMismatchException e) {
553             // just deleted it
554         }
555         rebuildGroups();
556         updateTimestamp(true, null);
557     }
558 
559     public synchronized void replace(Directory directory) throws IOException {
560         replace(directory, null, null);
561     }
562 
563     public synchronized void replace(Directory directory, Set<String> allGroups, Set<String> rootGroups)
564             throws IOException {
565         final Date ts = IndexUtils.getTimestamp(directory);
566         closeReaders();
567         deleteIndexFiles(false);
568         IndexUtils.copyDirectory(directory, indexDirectory);
569         openAndWarmup();
570         // reclaim the index as mine
571         storeDescriptor();
572         if (allGroups == null && rootGroups == null) {
573             rebuildGroups();
574         } else {
575             if (allGroups != null) {
576                 setAllGroups(allGroups);
577             }
578             if (rootGroups != null) {
579                 setRootGroups(rootGroups);
580             }
581         }
582         updateTimestamp(true, ts);
583         optimize();
584     }
585 
586     public synchronized void merge(Directory directory) throws IOException {
587         merge(directory, null);
588     }
589 
590     public synchronized void merge(Directory directory, DocumentFilter filter) throws IOException {
591         merge(directory, null, null, null);
592     }
593 
594     public synchronized void merge(
595             Directory directory, DocumentFilter filter, Set<String> allGroups, Set<String> rootGroups)
596             throws IOException {
597         final IndexSearcher s = acquireIndexSearcher();
598         try {
599             final IndexWriter w = getIndexWriter();
600             try (IndexReader directoryReader = DirectoryReader.open(directory)) {
601                 int numDocs = directoryReader.maxDoc();
602 
603                 Bits liveDocs = MultiBits.getLiveDocs(directoryReader);
604                 StoredFields storedFields = directoryReader.storedFields();
605                 for (int i = 0; i < numDocs; i++) {
606                     if (liveDocs != null && !liveDocs.get(i)) {
607                         continue;
608                     }
609 
610                     Document d = storedFields.document(i);
611                     if (filter != null && !filter.accept(d)) {
612                         continue;
613                     }
614 
615                     String uinfo = d.get(ArtifactInfo.UINFO);
616                     if (uinfo != null) {
617                         TopScoreDocCollector collector = TopScoreDocCollector.create(1, 1);
618                         s.search(new TermQuery(new Term(ArtifactInfo.UINFO, uinfo)), collector);
619                         if (collector.getTotalHits() == 0) {
620                             w.addDocument(IndexUtils.updateDocument(d, this, false));
621                         }
622                     } else {
623                         String deleted = d.get(ArtifactInfo.DELETED);
624 
625                         if (deleted != null) {
626                             // Deleting the document loses history that it was delete,
627                             // so incrementals wont work. Therefore, put the delete
628                             // document in as well
629                             w.deleteDocuments(new Term(ArtifactInfo.UINFO, deleted));
630                             w.addDocument(d);
631                         }
632                     }
633                 }
634 
635             } finally {
636                 commit();
637             }
638             if (allGroups == null && rootGroups == null) {
639                 rebuildGroups();
640             } else {
641                 if (rootGroups != null) {
642                     this.rootGroups.get().addAll(rootGroups);
643                 }
644                 if (allGroups != null) {
645                     this.allGroups.get().addAll(allGroups);
646                 }
647             }
648             Date mergedTimestamp = IndexUtils.getTimestamp(directory);
649 
650             if (getTimestamp() != null && mergedTimestamp != null && mergedTimestamp.after(getTimestamp())) {
651                 // we have both, keep the newest
652                 updateTimestamp(true, mergedTimestamp);
653             } else {
654                 updateTimestamp(true);
655             }
656             optimize();
657         } finally {
658             releaseIndexSearcher(s);
659         }
660     }
661 
662     private void closeReaders() throws CorruptIndexException, IOException {
663         if (searcherManager != null) {
664             searcherManager.close();
665             searcherManager = null;
666         }
667         if (indexWriter != null) {
668             indexWriter.close();
669             indexWriter = null;
670         }
671     }
672 
673     public GavCalculator getGavCalculator() {
674         return gavCalculator;
675     }
676 
677     public List<IndexCreator> getIndexCreators() {
678         return Collections.unmodifiableList(indexCreators);
679     }
680 
681     // groups
682 
683     public synchronized void rebuildGroups() throws IOException {
684         final IndexSearcher is = acquireIndexSearcher();
685         try {
686             final IndexReader r = is.getIndexReader();
687 
688             Set<String> rootGroups = new LinkedHashSet<>();
689             Set<String> allGroups = new LinkedHashSet<>();
690 
691             int numDocs = r.maxDoc();
692             Bits liveDocs = MultiBits.getLiveDocs(r);
693             StoredFields storedFields = r.storedFields();
694 
695             for (int i = 0; i < numDocs; i++) {
696                 if (liveDocs != null && !liveDocs.get(i)) {
697                     continue;
698                 }
699 
700                 Document d = storedFields.document(i);
701 
702                 String uinfo = d.get(ArtifactInfo.UINFO);
703 
704                 if (uinfo != null) {
705                     String group = uinfo.substring(0, uinfo.indexOf(ArtifactInfo.FS));
706                     int n = group.indexOf('.');
707                     rootGroups.add(n > -1 ? group.substring(0, n) : group);
708                     allGroups.add(group);
709                 }
710             }
711 
712             setRootGroups(rootGroups);
713             setAllGroups(allGroups);
714 
715             optimize();
716         } finally {
717             releaseIndexSearcher(is);
718         }
719     }
720 
721     public Set<String> getAllGroups() {
722         return allGroups.get();
723     }
724 
725     public synchronized void setAllGroups(Collection<String> groups) {
726         allGroups.set(new HashSet<>(groups));
727     }
728 
729     public Set<String> getRootGroups() throws IOException {
730         return rootGroups.get();
731     }
732 
733     public synchronized void setRootGroups(Collection<String> groups) {
734         rootGroups.set(new HashSet<>(groups));
735     }
736 
737     private final AtomicReference<Set<String>> rootGroups = new AtomicReference<>(new HashSet<>());
738 
739     private final AtomicReference<Set<String>> allGroups = new AtomicReference<>(new HashSet<>());
740 
741     @Override
742     public String toString() {
743         return id + " : " + timestamp;
744     }
745 
746     private static void unlockForcibly(final TrackingLockFactory lockFactory, final Directory dir) throws IOException {
747         // Warning: Not doable in lucene >= 5.3 consider to remove it as IndexWriter.unlock
748         // was always strongly non recommended by Lucene.
749         // For now try to do the best to simulate the IndexWriter.unlock at least on FSDirectory
750         // using FSLockFactory, the RAMDirectory uses SingleInstanceLockFactory.
751         // custom lock factory?
752         if (lockFactory != null) {
753             final Set<? extends Lock> emittedLocks = lockFactory.getEmittedLocks(IndexWriter.WRITE_LOCK_NAME);
754             for (Lock emittedLock : emittedLocks) {
755                 emittedLock.close();
756             }
757         }
758         if (dir instanceof FSDirectory) {
759             final FSDirectory fsdir = (FSDirectory) dir;
760             final Path dirPath = fsdir.getDirectory();
761             if (Files.isDirectory(dirPath)) {
762                 Path lockPath = dirPath.resolve(IndexWriter.WRITE_LOCK_NAME);
763                 try {
764                     lockPath = lockPath.toRealPath();
765                 } catch (IOException ioe) {
766                     // Not locked
767                     return;
768                 }
769                 try (FileChannel fc = FileChannel.open(lockPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
770                     final FileLock lck = fc.tryLock();
771                     if (lck == null) {
772                         // Still active
773                         throw new LockObtainFailedException("Lock held by another process: " + lockPath);
774                     } else {
775                         // Not held fine to release
776                         lck.close();
777                     }
778                 }
779                 Files.delete(lockPath);
780             }
781         }
782     }
783 }