1 package org.apache.maven.index.context;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.File;
23 import java.io.IOException;
24 import java.nio.channels.FileChannel;
25 import java.nio.channels.FileLock;
26 import java.nio.file.Files;
27 import java.nio.file.Path;
28 import java.nio.file.StandardOpenOption;
29 import java.util.Arrays;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.Date;
33 import java.util.LinkedHashSet;
34 import java.util.List;
35 import java.util.Set;
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.Term;
48 import org.apache.lucene.search.IndexSearcher;
49 import org.apache.lucene.search.SearcherManager;
50 import org.apache.lucene.search.TermQuery;
51 import org.apache.lucene.search.TopDocs;
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
67
68
69
70
71 public class DefaultIndexingContext
72 extends AbstractIndexingContext
73 {
74
75
76
77 private static final String INDEX_DIRECTORY = ".index";
78
79 public static final String FLD_DESCRIPTOR = "DESCRIPTOR";
80
81 public static final String FLD_DESCRIPTOR_CONTENTS = "NexusIndex";
82
83 public static final String FLD_IDXINFO = "IDXINFO";
84
85 public static final String VERSION = "1.0";
86
87 private static final Term DESCRIPTOR_TERM = new Term( FLD_DESCRIPTOR, FLD_DESCRIPTOR_CONTENTS );
88
89 private Directory indexDirectory;
90
91 private TrackingLockFactory lockFactory;
92
93 private File indexDirectoryFile;
94
95 private String id;
96
97 private boolean searchable;
98
99 private String repositoryId;
100
101 private File repository;
102
103 private String repositoryUrl;
104
105 private String indexUpdateUrl;
106
107 private NexusIndexWriter indexWriter;
108
109 private SearcherManager searcherManager;
110
111 private Date timestamp;
112
113 private List<? extends IndexCreator> indexCreators;
114
115
116
117
118
119
120 private GavCalculator gavCalculator;
121
122 private DefaultIndexingContext( String id,
123 String repositoryId,
124 File repository,
125 String repositoryUrl, String indexUpdateUrl,
126 List<? extends IndexCreator> indexCreators, Directory indexDirectory,
127 TrackingLockFactory lockFactory,
128 boolean reclaimIndex,
129 File indexDirectoryFile )
130 throws ExistingLuceneIndexMismatchException, IOException
131 {
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
156
157
158
159 for ( IndexCreator indexCreator : indexCreators )
160 {
161 indexCreator.getIndexerFields();
162 }
163
164 this.gavCalculator = new M2GavCalculator();
165
166 prepareIndex( reclaimIndex );
167
168 setIndexDirectoryFile( indexDirectoryFile );
169 }
170
171 private DefaultIndexingContext( String id, String repositoryId, File repository, File indexDirectoryFile,
172 TrackingLockFactory lockFactory, String repositoryUrl, String indexUpdateUrl,
173 List<? extends IndexCreator> indexCreators, boolean reclaimIndex )
174 throws IOException, ExistingLuceneIndexMismatchException
175 {
176 this( id, repositoryId, repository, repositoryUrl, indexUpdateUrl, indexCreators,
177 FSDirectory.open( indexDirectoryFile.toPath(), lockFactory ), lockFactory, reclaimIndex,
178 indexDirectoryFile );
179 }
180
181 public DefaultIndexingContext( String id, String repositoryId, File repository, File indexDirectoryFile,
182 String repositoryUrl, String indexUpdateUrl,
183 List<? extends IndexCreator> indexCreators, boolean reclaimIndex )
184 throws IOException, ExistingLuceneIndexMismatchException
185 {
186 this( id, repositoryId, repository, indexDirectoryFile, new TrackingLockFactory( FSLockFactory.getDefault() ),
187 repositoryUrl, indexUpdateUrl, indexCreators, reclaimIndex );
188 }
189
190 @Deprecated
191 public DefaultIndexingContext( String id, String repositoryId, File repository, Directory indexDirectory,
192 String repositoryUrl, String indexUpdateUrl,
193 List<? extends IndexCreator> indexCreators, boolean reclaimIndex )
194 throws IOException, ExistingLuceneIndexMismatchException
195 {
196 this( id, repositoryId, repository, repositoryUrl, indexUpdateUrl, indexCreators, indexDirectory, null,
197 reclaimIndex, indexDirectory instanceof FSDirectory
198 ? ( (FSDirectory) indexDirectory ).getDirectory().toFile() : null
199 );
200 }
201
202 public Directory getIndexDirectory()
203 {
204 return indexDirectory;
205 }
206
207
208
209
210
211 protected void setIndexDirectoryFile( File dir )
212 throws IOException
213 {
214 if ( dir == null )
215 {
216
217 this.indexDirectoryFile = Files.createTempDirectory( "mindexer-ctx" + id ).toFile();
218 this.indexDirectoryFile.deleteOnExit();
219 }
220 else
221 {
222 this.indexDirectoryFile = dir;
223 }
224 }
225
226 public File getIndexDirectoryFile()
227 {
228 return indexDirectoryFile;
229 }
230
231 private void prepareIndex( boolean reclaimIndex )
232 throws IOException, ExistingLuceneIndexMismatchException
233 {
234 if ( DirectoryReader.indexExists( indexDirectory ) )
235 {
236 try
237 {
238
239 try
240 {
241 indexDirectory.obtainLock( IndexWriter.WRITE_LOCK_NAME ).close();
242 }
243 catch ( LockObtainFailedException failed )
244 {
245 unlockForcibly( lockFactory, indexDirectory );
246 }
247
248 openAndWarmup();
249
250 checkAndUpdateIndexDescriptor( reclaimIndex );
251 }
252 catch ( IOException e )
253 {
254 if ( reclaimIndex )
255 {
256 prepareCleanIndex( true );
257 }
258 else
259 {
260 throw e;
261 }
262 }
263 }
264 else
265 {
266 prepareCleanIndex( false );
267 }
268
269 timestamp = IndexUtils.getTimestamp( indexDirectory );
270 }
271
272 private void prepareCleanIndex( boolean deleteExisting )
273 throws IOException
274 {
275 if ( deleteExisting )
276 {
277 closeReaders();
278
279
280 try
281 {
282 indexDirectory.obtainLock( IndexWriter.WRITE_LOCK_NAME ).close();
283 }
284 catch ( LockObtainFailedException failed )
285 {
286 unlockForcibly( lockFactory, indexDirectory );
287 }
288
289 deleteIndexFiles( true );
290 }
291
292 openAndWarmup();
293
294 if ( StringUtils.isEmpty( getRepositoryId() ) )
295 {
296 throw new IllegalArgumentException( "The repositoryId cannot be null when creating new repository!" );
297 }
298
299 storeDescriptor();
300 }
301
302 private void checkAndUpdateIndexDescriptor( boolean reclaimIndex )
303 throws IOException, ExistingLuceneIndexMismatchException
304 {
305 if ( reclaimIndex )
306 {
307
308 storeDescriptor();
309 return;
310 }
311
312
313 if ( getSize() > 0 )
314 {
315 final TopScoreDocCollector collector = TopScoreDocCollector.create( 1, Integer.MAX_VALUE );
316 final IndexSearcher indexSearcher = acquireIndexSearcher();
317 try
318 {
319 indexSearcher.search( new TermQuery( DESCRIPTOR_TERM ), collector );
320
321 if ( collector.getTotalHits() == 0 )
322 {
323 throw new ExistingLuceneIndexMismatchException(
324 "The existing index has no NexusIndexer descriptor" );
325 }
326
327 if ( collector.getTotalHits() > 1 )
328 {
329
330 storeDescriptor();
331 }
332 else
333 {
334
335 Document descriptor = indexSearcher.doc( collector.topDocs().scoreDocs[0].doc );
336 String[] h = StringUtils.split( descriptor.get( FLD_IDXINFO ), ArtifactInfo.FS );
337
338 String repoId = h[1];
339
340
341
342
343
344
345
346
347 if ( getRepositoryId() == null )
348 {
349 repositoryId = repoId;
350 }
351 else if ( !getRepositoryId().equals( repoId ) )
352 {
353 throw new ExistingLuceneIndexMismatchException( "The existing index is for repository "
354 + "[" + repoId + "] and not for repository [" + getRepositoryId() + "]" );
355 }
356 }
357 }
358 finally
359 {
360 releaseIndexSearcher( indexSearcher );
361 }
362 }
363 }
364
365 private void storeDescriptor()
366 throws IOException
367 {
368 Document hdr = new Document();
369
370 hdr.add( new Field( FLD_DESCRIPTOR, FLD_DESCRIPTOR_CONTENTS, IndexerField.KEYWORD_STORED ) );
371
372 hdr.add( new StoredField( FLD_IDXINFO, VERSION + ArtifactInfo.FS + getRepositoryId() ) );
373
374 IndexWriter w = getIndexWriter();
375
376 w.updateDocument( DESCRIPTOR_TERM, hdr );
377
378 w.commit();
379 }
380
381 private void deleteIndexFiles( boolean full )
382 throws IOException
383 {
384 if ( indexDirectory != null )
385 {
386 String[] names = indexDirectory.listAll();
387
388 if ( names != null )
389 {
390
391 for ( String name : names )
392 {
393 if ( !( name.equals( INDEX_PACKER_PROPERTIES_FILE )
394 || name.equals( INDEX_UPDATER_PROPERTIES_FILE ) ) )
395 {
396 indexDirectory.deleteFile( name );
397 }
398 }
399 }
400
401 if ( full )
402 {
403 try
404 {
405 indexDirectory.deleteFile( INDEX_PACKER_PROPERTIES_FILE );
406 }
407 catch ( IOException ioe )
408 {
409
410 }
411
412 try
413 {
414 indexDirectory.deleteFile( INDEX_UPDATER_PROPERTIES_FILE );
415 }
416 catch ( IOException ioe )
417 {
418
419 }
420 }
421
422 IndexUtils.deleteTimestamp( indexDirectory );
423 }
424 }
425
426
427
428 public boolean isSearchable()
429 {
430 return searchable;
431 }
432
433 public void setSearchable( boolean searchable )
434 {
435 this.searchable = searchable;
436 }
437
438 public String getId()
439 {
440 return id;
441 }
442
443 public void updateTimestamp()
444 throws IOException
445 {
446 updateTimestamp( false );
447 }
448
449 public void updateTimestamp( boolean save )
450 throws IOException
451 {
452 updateTimestamp( save, new Date() );
453 }
454
455 public void updateTimestamp( boolean save, Date timestamp )
456 throws IOException
457 {
458 this.timestamp = timestamp;
459
460 if ( save )
461 {
462 IndexUtils.updateTimestamp( indexDirectory, getTimestamp() );
463 }
464 }
465
466 public Date getTimestamp()
467 {
468 return timestamp;
469 }
470
471 public int getSize()
472 throws IOException
473 {
474 final IndexSearcher is = acquireIndexSearcher();
475 try
476 {
477 return is.getIndexReader().numDocs();
478 }
479 finally
480 {
481 releaseIndexSearcher( is );
482 }
483 }
484
485 public String getRepositoryId()
486 {
487 return repositoryId;
488 }
489
490 public File getRepository()
491 {
492 return repository;
493 }
494
495 public String getRepositoryUrl()
496 {
497 return repositoryUrl;
498 }
499
500 public String getIndexUpdateUrl()
501 {
502 if ( repositoryUrl != null )
503 {
504 if ( indexUpdateUrl == null || indexUpdateUrl.trim().length() == 0 )
505 {
506 return repositoryUrl + ( repositoryUrl.endsWith( "/" ) ? "" : "/" ) + INDEX_DIRECTORY;
507 }
508 }
509 return indexUpdateUrl;
510 }
511
512 public Analyzer getAnalyzer()
513 {
514 return new NexusAnalyzer();
515 }
516
517 protected void openAndWarmup()
518 throws IOException
519 {
520
521 if ( indexWriter != null )
522 {
523 indexWriter.close();
524
525 indexWriter = null;
526 }
527 if ( searcherManager != null )
528 {
529 searcherManager.close();
530
531 searcherManager = null;
532 }
533
534 this.indexWriter = new NexusIndexWriter( getIndexDirectory(), getWriterConfig() );
535 this.indexWriter.commit();
536 this.searcherManager = new SearcherManager( indexWriter, false, false, new NexusIndexSearcherFactory( this ) );
537 }
538
539
540
541
542
543
544 protected IndexWriterConfig getWriterConfig()
545 {
546 return NexusIndexWriter.defaultConfig();
547 }
548
549 public IndexWriter getIndexWriter()
550 throws IOException
551 {
552 return indexWriter;
553 }
554
555 public IndexSearcher acquireIndexSearcher()
556 throws IOException
557 {
558
559 searcherManager.maybeRefresh();
560 return searcherManager.acquire();
561 }
562
563 public void releaseIndexSearcher( final IndexSearcher is )
564 throws IOException
565 {
566 if ( is == null )
567 {
568 return;
569 }
570 searcherManager.release( is );
571 }
572
573 public void commit()
574 throws IOException
575 {
576 getIndexWriter().commit();
577 }
578
579 public void rollback()
580 throws IOException
581 {
582 getIndexWriter().rollback();
583 }
584
585 public synchronized void optimize()
586 throws CorruptIndexException, IOException
587 {
588 commit();
589 }
590
591 public synchronized void close( boolean deleteFiles )
592 throws IOException
593 {
594 if ( indexDirectory != null )
595 {
596 IndexUtils.updateTimestamp( indexDirectory, getTimestamp() );
597 closeReaders();
598 if ( deleteFiles )
599 {
600 deleteIndexFiles( true );
601 }
602 indexDirectory.close();
603 }
604 indexDirectory = null;
605 }
606
607 public synchronized void purge()
608 throws IOException
609 {
610 closeReaders();
611 deleteIndexFiles( true );
612 try
613 {
614 prepareIndex( true );
615 }
616 catch ( ExistingLuceneIndexMismatchException e )
617 {
618
619 }
620 rebuildGroups();
621 updateTimestamp( true, null );
622 }
623
624 public synchronized void replace( Directory directory )
625 throws IOException
626 {
627 replace( directory, null, null );
628 }
629
630 public synchronized void replace( Directory directory, Set<String> allGroups, Set<String> rootGroups )
631 throws IOException
632 {
633 final Date ts = IndexUtils.getTimestamp( directory );
634 closeReaders();
635 deleteIndexFiles( false );
636 IndexUtils.copyDirectory( directory, indexDirectory );
637 openAndWarmup();
638
639 storeDescriptor();
640 if ( allGroups == null && rootGroups == null )
641 {
642 rebuildGroups();
643 }
644 else
645 {
646 if ( allGroups != null )
647 {
648 setAllGroups( allGroups );
649 }
650 if ( rootGroups != null )
651 {
652 setRootGroups( rootGroups );
653 }
654 }
655 updateTimestamp( true, ts );
656 optimize();
657 }
658
659 public synchronized void merge( Directory directory )
660 throws IOException
661 {
662 merge( directory, null );
663 }
664
665 public synchronized void merge( Directory directory, DocumentFilter filter )
666 throws IOException
667 {
668 final IndexSearcher s = acquireIndexSearcher();
669 try
670 {
671 final IndexWriter w = getIndexWriter();
672 try ( IndexReader directoryReader = DirectoryReader.open( directory ) )
673 {
674 TopScoreDocCollector collector;
675 int numDocs = directoryReader.maxDoc();
676
677 Bits liveDocs = MultiBits.getLiveDocs( directoryReader );
678 for ( int i = 0; i < numDocs; i++ )
679 {
680 if ( liveDocs != null && !liveDocs.get( i ) )
681 {
682 continue;
683 }
684
685 Document d = directoryReader.document( i );
686 if ( filter != null && !filter.accept( d ) )
687 {
688 continue;
689 }
690
691 String uinfo = d.get( ArtifactInfo.UINFO );
692 if ( uinfo != null )
693 {
694 collector = TopScoreDocCollector.create( 1, Integer.MAX_VALUE );
695 s.search( new TermQuery( new Term( ArtifactInfo.UINFO, uinfo ) ), collector );
696 if ( collector.getTotalHits() == 0 )
697 {
698 w.addDocument( IndexUtils.updateDocument( d, this, false ) );
699 }
700 }
701 else
702 {
703 String deleted = d.get( ArtifactInfo.DELETED );
704
705 if ( deleted != null )
706 {
707
708
709
710 w.deleteDocuments( new Term( ArtifactInfo.UINFO, deleted ) );
711 w.addDocument( d );
712 }
713 }
714 }
715
716 }
717 finally
718 {
719 commit();
720 }
721
722 rebuildGroups();
723 Date mergedTimestamp = IndexUtils.getTimestamp( directory );
724
725 if ( getTimestamp() != null && mergedTimestamp != null && mergedTimestamp.after( getTimestamp() ) )
726 {
727
728 updateTimestamp( true, mergedTimestamp );
729 }
730 else
731 {
732 updateTimestamp( true );
733 }
734 optimize();
735 }
736 finally
737 {
738 releaseIndexSearcher( s );
739 }
740 }
741
742 private void closeReaders()
743 throws CorruptIndexException, IOException
744 {
745 if ( searcherManager != null )
746 {
747 searcherManager.close();
748 searcherManager = null;
749 }
750 if ( indexWriter != null )
751 {
752 indexWriter.close();
753 indexWriter = null;
754 }
755 }
756
757 public GavCalculator getGavCalculator()
758 {
759 return gavCalculator;
760 }
761
762 public List<IndexCreator> getIndexCreators()
763 {
764 return Collections.unmodifiableList( indexCreators );
765 }
766
767
768
769 public synchronized void rebuildGroups()
770 throws IOException
771 {
772 final IndexSearcher is = acquireIndexSearcher();
773 try
774 {
775 final IndexReader r = is.getIndexReader();
776
777 Set<String> rootGroups = new LinkedHashSet<>();
778 Set<String> allGroups = new LinkedHashSet<>();
779
780 int numDocs = r.maxDoc();
781 Bits liveDocs = MultiBits.getLiveDocs( r );
782
783 for ( int i = 0; i < numDocs; i++ )
784 {
785 if ( liveDocs != null && !liveDocs.get( i ) )
786 {
787 continue;
788 }
789
790 Document d = r.document( i );
791
792 String uinfo = d.get( ArtifactInfo.UINFO );
793
794 if ( uinfo != null )
795 {
796 ArtifactInfo info = IndexUtils.constructArtifactInfo( d, this );
797 rootGroups.add( info.getRootGroup() );
798 allGroups.add( info.getGroupId() );
799 }
800 }
801
802 setRootGroups( rootGroups );
803 setAllGroups( allGroups );
804
805 optimize();
806 }
807 finally
808 {
809 releaseIndexSearcher( is );
810 }
811 }
812
813 public Set<String> getAllGroups()
814 throws IOException
815 {
816 return getGroups( ArtifactInfo.ALL_GROUPS, ArtifactInfo.ALL_GROUPS_VALUE, ArtifactInfo.ALL_GROUPS_LIST );
817 }
818
819 public synchronized void setAllGroups( Collection<String> groups )
820 throws IOException
821 {
822 setGroups( groups, ArtifactInfo.ALL_GROUPS, ArtifactInfo.ALL_GROUPS_VALUE, ArtifactInfo.ALL_GROUPS_LIST );
823 commit();
824 }
825
826 public Set<String> getRootGroups()
827 throws IOException
828 {
829 return getGroups( ArtifactInfo.ROOT_GROUPS, ArtifactInfo.ROOT_GROUPS_VALUE, ArtifactInfo.ROOT_GROUPS_LIST );
830 }
831
832 public synchronized void setRootGroups( Collection<String> groups )
833 throws IOException
834 {
835 setGroups( groups, ArtifactInfo.ROOT_GROUPS, ArtifactInfo.ROOT_GROUPS_VALUE, ArtifactInfo.ROOT_GROUPS_LIST );
836 commit();
837 }
838
839 protected Set<String> getGroups( String field, String filedValue, String listField )
840 throws IOException, CorruptIndexException
841 {
842 final TopScoreDocCollector collector = TopScoreDocCollector.create( 1, Integer.MAX_VALUE );
843 final IndexSearcher indexSearcher = acquireIndexSearcher();
844 try
845 {
846 indexSearcher.search( new TermQuery( new Term( field, filedValue ) ), collector );
847 TopDocs topDocs = collector.topDocs();
848
849
850 Set<String> groups = new LinkedHashSet<String>( (int) Math.max( 10L, topDocs.totalHits.value ) );
851 if ( topDocs.totalHits.value > 0 )
852 {
853 Document doc = indexSearcher.doc( topDocs.scoreDocs[0].doc );
854 String groupList = doc.get( listField );
855 if ( groupList != null )
856 {
857 groups.addAll( Arrays.asList( groupList.split( "\\|" ) ) );
858 }
859 }
860 return groups;
861 }
862 finally
863 {
864 releaseIndexSearcher( indexSearcher );
865 }
866 }
867
868 protected void setGroups( Collection<String> groups, String groupField, String groupFieldValue,
869 String groupListField )
870 throws IOException, CorruptIndexException
871 {
872 final IndexWriter w = getIndexWriter();
873 w.updateDocument( new Term( groupField, groupFieldValue ),
874 createGroupsDocument( groups, groupField, groupFieldValue, groupListField ) );
875 }
876
877 protected Document createGroupsDocument( Collection<String> groups, String field, String fieldValue,
878 String listField )
879 {
880 final Document groupDoc = new Document();
881 groupDoc.add( new Field( field, fieldValue, IndexerField.KEYWORD_STORED ) );
882 groupDoc.add( new StoredField( listField, ArtifactInfo.lst2str( groups ) ) );
883 return groupDoc;
884 }
885
886 @Override
887 public String toString()
888 {
889 return id + " : " + timestamp;
890 }
891
892 private static void unlockForcibly( final TrackingLockFactory lockFactory, final Directory dir )
893 throws IOException
894 {
895
896
897
898
899
900 if ( lockFactory != null )
901 {
902 final Set<? extends Lock> emittedLocks = lockFactory.getEmittedLocks( IndexWriter.WRITE_LOCK_NAME );
903 for ( Lock emittedLock : emittedLocks )
904 {
905 emittedLock.close();
906 }
907 }
908 if ( dir instanceof FSDirectory )
909 {
910 final FSDirectory fsdir = (FSDirectory) dir;
911 final Path dirPath = fsdir.getDirectory();
912 if ( Files.isDirectory( dirPath ) )
913 {
914 Path lockPath = dirPath.resolve( IndexWriter.WRITE_LOCK_NAME );
915 try
916 {
917 lockPath = lockPath.toRealPath();
918 }
919 catch ( IOException ioe )
920 {
921
922 return;
923 }
924 try ( FileChannel fc = FileChannel.open( lockPath, StandardOpenOption.CREATE,
925 StandardOpenOption.WRITE ) )
926 {
927 final FileLock lck = fc.tryLock();
928 if ( lck == null )
929 {
930
931 throw new LockObtainFailedException( "Lock held by another process: " + lockPath );
932 }
933 else
934 {
935
936 lck.close();
937 }
938 }
939 Files.delete( lockPath );
940 }
941 }
942 }
943 }