View Javadoc

1   package org.apache.maven.index.updater;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0    
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.BufferedInputStream;
23  import java.io.BufferedOutputStream;
24  import java.io.BufferedReader;
25  import java.io.File;
26  import java.io.FileInputStream;
27  import java.io.FileNotFoundException;
28  import java.io.FileOutputStream;
29  import java.io.IOException;
30  import java.io.InputStream;
31  import java.io.InputStreamReader;
32  import java.io.OutputStream;
33  import java.io.OutputStreamWriter;
34  import java.io.Writer;
35  import java.text.ParseException;
36  import java.text.SimpleDateFormat;
37  import java.util.ArrayList;
38  import java.util.Date;
39  import java.util.List;
40  import java.util.Properties;
41  import java.util.TimeZone;
42  import java.util.zip.ZipEntry;
43  import java.util.zip.ZipInputStream;
44  
45  import org.apache.lucene.document.Document;
46  import org.apache.lucene.index.CorruptIndexException;
47  import org.apache.lucene.index.IndexReader;
48  import org.apache.lucene.index.IndexWriter;
49  import org.apache.lucene.store.Directory;
50  import org.apache.lucene.store.FSDirectory;
51  import org.apache.lucene.store.IndexOutput;
52  import org.apache.lucene.store.LockObtainFailedException;
53  import org.apache.maven.index.context.DocumentFilter;
54  import org.apache.maven.index.context.IndexUtils;
55  import org.apache.maven.index.context.IndexingContext;
56  import org.apache.maven.index.context.NexusAnalyzer;
57  import org.apache.maven.index.context.NexusIndexWriter;
58  import org.apache.maven.index.fs.Lock;
59  import org.apache.maven.index.fs.Locker;
60  import org.apache.maven.index.incremental.IncrementalHandler;
61  import org.apache.maven.index.updater.IndexDataReader.IndexDataReadResult;
62  import org.codehaus.plexus.component.annotations.Component;
63  import org.codehaus.plexus.component.annotations.Requirement;
64  import org.codehaus.plexus.logging.AbstractLogEnabled;
65  import org.codehaus.plexus.util.FileUtils;
66  import org.codehaus.plexus.util.IOUtil;
67  import org.codehaus.plexus.util.io.RawInputStreamFacade;
68  
69  /**
70   * A default index updater implementation
71   * 
72   * @author Jason van Zyl
73   * @author Eugene Kuleshov
74   */
75  @Component( role = IndexUpdater.class )
76  public class DefaultIndexUpdater
77      extends AbstractLogEnabled
78      implements IndexUpdater
79  {
80  
81      @Requirement( role = IncrementalHandler.class )
82      IncrementalHandler incrementalHandler;
83  
84      @Requirement( role = IndexUpdateSideEffect.class )
85      private List<IndexUpdateSideEffect> sideEffects;
86  
87      public DefaultIndexUpdater( final IncrementalHandler handler, final List<IndexUpdateSideEffect> mySideeffects )
88      {
89          incrementalHandler = handler;
90          sideEffects = mySideeffects;
91      }
92  
93      public DefaultIndexUpdater()
94      {
95  
96      }
97  
98      public IndexUpdateResult fetchAndUpdateIndex( final IndexUpdateRequest updateRequest )
99          throws IOException
100     {
101         IndexUpdateResult result = new IndexUpdateResult();
102 
103         IndexingContext context = updateRequest.getIndexingContext();
104 
105         ResourceFetcher fetcher = null;
106 
107         if ( !updateRequest.isOffline() )
108         {
109             fetcher = updateRequest.getResourceFetcher();
110 
111             // If no resource fetcher passed in, use the wagon fetcher by default
112             // and put back in request for future use
113             if ( fetcher == null )
114             {
115                 throw new IOException( "Update of the index without provided ResourceFetcher is impossible." );
116             }
117 
118             fetcher.connect( context.getId(), context.getIndexUpdateUrl() );
119         }
120 
121         File cacheDir = updateRequest.getLocalIndexCacheDir();
122         Locker locker = updateRequest.getLocker();
123         Lock lock = locker != null && cacheDir != null ? locker.lock( cacheDir ) : null;
124         try
125         {
126             if ( cacheDir != null )
127             {
128                 LocalCacheIndexAdaptor cache = new LocalCacheIndexAdaptor( cacheDir, result );
129 
130                 if ( !updateRequest.isOffline() )
131                 {
132                     cacheDir.mkdirs();
133 
134                     try
135                     {
136                         fetchAndUpdateIndex( updateRequest, fetcher, cache );
137                         cache.commit();
138                     }
139                     finally
140                     {
141                         fetcher.disconnect();
142                     }
143                 }
144 
145                 fetcher = cache.getFetcher();
146             }
147             else if ( updateRequest.isOffline() )
148             {
149                 throw new IllegalArgumentException( "LocalIndexCacheDir can not be null in offline mode" );
150             }
151 
152             try
153             {
154                 if ( !updateRequest.isCacheOnly() )
155                 {
156                     LuceneIndexAdaptor target = new LuceneIndexAdaptor( updateRequest );
157                     result.setTimestamp( fetchAndUpdateIndex( updateRequest, fetcher, target ) );
158                     target.commit();
159                 }
160             }
161             finally
162             {
163                 fetcher.disconnect();
164             }
165         }
166         finally
167         {
168             if ( lock != null )
169             {
170                 lock.release();
171             }
172         }
173 
174         return result;
175     }
176 
177     private Date loadIndexDirectory( final IndexUpdateRequest updateRequest, final ResourceFetcher fetcher,
178                                      final boolean merge, final String remoteIndexFile )
179         throws IOException
180     {
181         File indexDir = File.createTempFile( remoteIndexFile, ".dir" );
182         indexDir.delete();
183         indexDir.mkdirs();
184 
185         FSDirectory directory = FSDirectory.open( indexDir );
186 
187         BufferedInputStream is = null;
188 
189         try
190         {
191             is = new BufferedInputStream( fetcher.retrieve( remoteIndexFile ) );
192 
193             Date timestamp = null;
194 
195             if ( remoteIndexFile.endsWith( ".gz" ) )
196             {
197                 timestamp = unpackIndexData( is, directory, //
198                     updateRequest.getIndexingContext() );
199             }
200             else
201             {
202                 // legacy transfer format
203                 timestamp = unpackIndexArchive( is, directory, //
204                     updateRequest.getIndexingContext() );
205             }
206 
207             if ( updateRequest.getDocumentFilter() != null )
208             {
209                 filterDirectory( directory, updateRequest.getDocumentFilter() );
210             }
211 
212             if ( merge )
213             {
214                 updateRequest.getIndexingContext().merge( directory );
215             }
216             else
217             {
218                 updateRequest.getIndexingContext().replace( directory );
219             }
220             if ( sideEffects != null && sideEffects.size() > 0 )
221             {
222                 getLogger().info( IndexUpdateSideEffect.class.getName() + " extensions found: " + sideEffects.size() );
223                 for ( IndexUpdateSideEffect sideeffect : sideEffects )
224                 {
225                     sideeffect.updateIndex( directory, updateRequest.getIndexingContext(), merge );
226                 }
227             }
228 
229             return timestamp;
230         }
231         finally
232         {
233             IOUtil.close( is );
234 
235             if ( directory != null )
236             {
237                 directory.close();
238             }
239 
240             try
241             {
242                 FileUtils.deleteDirectory( indexDir );
243             }
244             catch ( IOException ex )
245             {
246                 // ignore
247             }
248         }
249     }
250 
251     /**
252      * Unpack legacy index archive into a specified Lucene <code>Directory</code>
253      * 
254      * @param is a <code>ZipInputStream</code> with index data
255      * @param directory Lucene <code>Directory</code> to unpack index data to
256      * @return {@link Date} of the index update or null if it can't be read
257      */
258     public static Date unpackIndexArchive( final InputStream is, final Directory directory,
259                                            final IndexingContext context )
260         throws IOException
261     {
262         File indexArchive = File.createTempFile( "nexus-index", "" );
263 
264         File indexDir = new File( indexArchive.getAbsoluteFile().getParentFile(), indexArchive.getName() + ".dir" );
265 
266         indexDir.mkdirs();
267 
268         FSDirectory fdir = FSDirectory.open( indexDir );
269 
270         try
271         {
272             unpackDirectory( fdir, is );
273             copyUpdatedDocuments( fdir, directory, context );
274 
275             Date timestamp = IndexUtils.getTimestamp( fdir );
276             IndexUtils.updateTimestamp( directory, timestamp );
277             return timestamp;
278         }
279         finally
280         {
281             IndexUtils.close( fdir );
282             indexArchive.delete();
283             IndexUtils.delete( indexDir );
284         }
285     }
286 
287     private static void unpackDirectory( final Directory directory, final InputStream is )
288         throws IOException
289     {
290         byte[] buf = new byte[4096];
291 
292         ZipEntry entry;
293 
294         ZipInputStream zis = null;
295 
296         try
297         {
298             zis = new ZipInputStream( is );
299 
300             while ( ( entry = zis.getNextEntry() ) != null )
301             {
302                 if ( entry.isDirectory() || entry.getName().indexOf( '/' ) > -1 )
303                 {
304                     continue;
305                 }
306 
307                 IndexOutput io = directory.createOutput( entry.getName() );
308                 try
309                 {
310                     int n = 0;
311 
312                     while ( ( n = zis.read( buf ) ) != -1 )
313                     {
314                         io.writeBytes( buf, n );
315                     }
316                 }
317                 finally
318                 {
319                     IndexUtils.close( io );
320                 }
321             }
322         }
323         finally
324         {
325             IndexUtils.close( zis );
326         }
327     }
328 
329     private static void copyUpdatedDocuments( final Directory sourcedir, final Directory targetdir,
330                                               final IndexingContext context )
331         throws CorruptIndexException, LockObtainFailedException, IOException
332     {
333         IndexWriter w = null;
334         IndexReader r = null;
335         try
336         {
337             r = IndexReader.open( sourcedir );
338             w = new NexusIndexWriter( targetdir, new NexusAnalyzer(), true );
339 
340             for ( int i = 0; i < r.maxDoc(); i++ )
341             {
342                 if ( !r.isDeleted( i ) )
343                 {
344                     w.addDocument( IndexUtils.updateDocument( r.document( i ), context ) );
345                 }
346             }
347 
348             w.optimize();
349             w.commit();
350         }
351         finally
352         {
353             IndexUtils.close( w );
354             IndexUtils.close( r );
355         }
356     }
357 
358     private static void filterDirectory( final Directory directory, final DocumentFilter filter )
359         throws IOException
360     {
361         IndexReader r = null;
362         try
363         {
364             // explicitly RW reader needed
365             r = IndexReader.open( directory, false );
366 
367             int numDocs = r.maxDoc();
368 
369             for ( int i = 0; i < numDocs; i++ )
370             {
371                 if ( r.isDeleted( i ) )
372                 {
373                     continue;
374                 }
375 
376                 Document d = r.document( i );
377 
378                 if ( !filter.accept( d ) )
379                 {
380                     r.deleteDocument( i );
381                 }
382             }
383         }
384         finally
385         {
386             IndexUtils.close( r );
387         }
388 
389         IndexWriter w = null;
390         try
391         {
392             // analyzer is unimportant, since we are not adding/searching to/on index, only reading/deleting
393             w = new NexusIndexWriter( directory, new NexusAnalyzer(), false );
394 
395             w.optimize();
396 
397             w.commit();
398         }
399         finally
400         {
401             IndexUtils.close( w );
402         }
403     }
404 
405     private Properties loadIndexProperties( final File indexDirectoryFile, final String remoteIndexPropertiesName )
406     {
407         File indexProperties = new File( indexDirectoryFile, remoteIndexPropertiesName );
408 
409         FileInputStream fis = null;
410 
411         try
412         {
413             Properties properties = new Properties();
414 
415             fis = new FileInputStream( indexProperties );
416 
417             properties.load( fis );
418 
419             return properties;
420         }
421         catch ( IOException e )
422         {
423             getLogger().debug( "Unable to read remote properties stored locally", e );
424         }
425         finally
426         {
427             IOUtil.close( fis );
428         }
429 
430         return null;
431     }
432 
433     private void storeIndexProperties( final File dir, final String indexPropertiesName, final Properties properties )
434         throws IOException
435     {
436         File file = new File( dir, indexPropertiesName );
437 
438         if ( properties != null )
439         {
440             OutputStream os = new BufferedOutputStream( new FileOutputStream( file ) );
441             try
442             {
443                 properties.store( os, null );
444             }
445             finally
446             {
447                 IOUtil.close( os );
448             }
449         }
450         else
451         {
452             file.delete();
453         }
454     }
455 
456     private Properties downloadIndexProperties( final ResourceFetcher fetcher )
457         throws IOException
458     {
459         InputStream fis = fetcher.retrieve( IndexingContext.INDEX_REMOTE_PROPERTIES_FILE );
460 
461         try
462         {
463             Properties properties = new Properties();
464 
465             properties.load( fis );
466 
467             return properties;
468         }
469         finally
470         {
471             IOUtil.close( fis );
472         }
473     }
474 
475     public Date getTimestamp( final Properties properties, final String key )
476     {
477         String indexTimestamp = properties.getProperty( key );
478 
479         if ( indexTimestamp != null )
480         {
481             try
482             {
483                 SimpleDateFormat df = new SimpleDateFormat( IndexingContext.INDEX_TIME_FORMAT );
484                 df.setTimeZone( TimeZone.getTimeZone( "GMT" ) );
485                 return df.parse( indexTimestamp );
486             }
487             catch ( ParseException ex )
488             {
489             }
490         }
491         return null;
492     }
493 
494     /**
495      * Unpack index data using specified Lucene Index writer
496      * 
497      * @param is an input stream to unpack index data from
498      * @param w a writer to save index data
499      * @param ics a collection of index creators for updating unpacked documents.
500      */
501     public static Date unpackIndexData( final InputStream is, final Directory d, final IndexingContext context )
502         throws IOException
503     {
504         NexusIndexWriter w = new NexusIndexWriter( d, new NexusAnalyzer(), true );
505         try
506         {
507             IndexDataReader dr = new IndexDataReader( is );
508 
509             IndexDataReadResult result = dr.readIndex( w, context );
510 
511             return result.getTimestamp();
512         }
513         finally
514         {
515             IndexUtils.close( w );
516         }
517     }
518 
519     /**
520      * Filesystem-based ResourceFetcher implementation
521      */
522     public static class FileFetcher
523         implements ResourceFetcher
524     {
525         private final File basedir;
526 
527         public FileFetcher( File basedir )
528         {
529             this.basedir = basedir;
530         }
531 
532         public void connect( String id, String url )
533             throws IOException
534         {
535             // don't need to do anything
536         }
537 
538         public void disconnect()
539             throws IOException
540         {
541             // don't need to do anything
542         }
543 
544         public void retrieve( String name, File targetFile )
545             throws IOException, FileNotFoundException
546         {
547             FileUtils.copyFile( getFile( name ), targetFile );
548 
549         }
550 
551         public InputStream retrieve( String name )
552             throws IOException, FileNotFoundException
553         {
554             return new FileInputStream( getFile( name ) );
555         }
556 
557         private File getFile( String name )
558         {
559             return new File( basedir, name );
560         }
561 
562     }
563 
564     private abstract class IndexAdaptor
565     {
566         protected final File dir;
567 
568         protected Properties properties;
569 
570         protected IndexAdaptor( File dir )
571         {
572             this.dir = dir;
573         }
574 
575         public abstract Properties getProperties();
576 
577         public abstract void storeProperties()
578             throws IOException;
579 
580         public abstract void addIndexChunk( ResourceFetcher source, String filename )
581             throws IOException;
582 
583         public abstract Date setIndexFile( ResourceFetcher source, String string )
584             throws IOException;
585 
586         public Properties setProperties( ResourceFetcher source )
587             throws IOException
588         {
589             this.properties = downloadIndexProperties( source );
590             return properties;
591         }
592 
593         public abstract Date getTimestamp();
594 
595         public void commit()
596             throws IOException
597         {
598             storeProperties();
599         }
600     }
601 
602     private class LuceneIndexAdaptor
603         extends IndexAdaptor
604     {
605         private final IndexUpdateRequest updateRequest;
606 
607         public LuceneIndexAdaptor( IndexUpdateRequest updateRequest )
608         {
609             super( updateRequest.getIndexingContext().getIndexDirectoryFile() );
610             this.updateRequest = updateRequest;
611         }
612 
613         public Properties getProperties()
614         {
615             if ( properties == null )
616             {
617                 properties = loadIndexProperties( dir, IndexingContext.INDEX_UPDATER_PROPERTIES_FILE );
618             }
619             return properties;
620         }
621 
622         public void storeProperties()
623             throws IOException
624         {
625             storeIndexProperties( dir, IndexingContext.INDEX_UPDATER_PROPERTIES_FILE, properties );
626         }
627 
628         public Date getTimestamp()
629         {
630             return updateRequest.getIndexingContext().getTimestamp();
631         }
632 
633         public void addIndexChunk( ResourceFetcher source, String filename )
634             throws IOException
635         {
636             loadIndexDirectory( updateRequest, source, true, filename );
637         }
638 
639         public Date setIndexFile( ResourceFetcher source, String filename )
640             throws IOException
641         {
642             return loadIndexDirectory( updateRequest, source, false, filename );
643         }
644 
645         public void commit()
646             throws IOException
647         {
648             super.commit();
649 
650             updateRequest.getIndexingContext().commit();
651         }
652 
653     }
654 
655     private class LocalCacheIndexAdaptor
656         extends IndexAdaptor
657     {
658         private static final String CHUNKS_FILENAME = "chunks.lst";
659 
660         private static final String CHUNKS_FILE_ENCODING = "UTF-8";
661 
662         private final IndexUpdateResult result;
663 
664         private final ArrayList<String> newChunks = new ArrayList<String>();
665 
666         public LocalCacheIndexAdaptor( File dir, IndexUpdateResult result )
667         {
668             super( dir );
669             this.result = result;
670         }
671 
672         public Properties getProperties()
673         {
674             if ( properties == null )
675             {
676                 properties = loadIndexProperties( dir, IndexingContext.INDEX_REMOTE_PROPERTIES_FILE );
677             }
678             return properties;
679         }
680 
681         public void storeProperties()
682             throws IOException
683         {
684             storeIndexProperties( dir, IndexingContext.INDEX_REMOTE_PROPERTIES_FILE, properties );
685         }
686 
687         public Date getTimestamp()
688         {
689             Properties properties = getProperties();
690             if ( properties == null )
691             {
692                 return null;
693             }
694 
695             Date timestamp = DefaultIndexUpdater.this.getTimestamp( properties, IndexingContext.INDEX_TIMESTAMP );
696 
697             if ( timestamp == null )
698             {
699                 timestamp = DefaultIndexUpdater.this.getTimestamp( properties, IndexingContext.INDEX_LEGACY_TIMESTAMP );
700             }
701 
702             return timestamp;
703         }
704 
705         public void addIndexChunk( ResourceFetcher source, String filename )
706             throws IOException
707         {
708             File chunk = new File( dir, filename );
709             FileUtils.copyStreamToFile( new RawInputStreamFacade( source.retrieve( filename ) ), chunk );
710             newChunks.add( filename );
711         }
712 
713         public Date setIndexFile( ResourceFetcher source, String filename )
714             throws IOException
715         {
716             cleanCacheDirectory( dir );
717 
718             result.setFullUpdate( true );
719 
720             File target = new File( dir, filename );
721             FileUtils.copyStreamToFile( new RawInputStreamFacade( source.retrieve( filename ) ), target );
722 
723             return null;
724         }
725 
726         @Override
727         public void commit()
728             throws IOException
729         {
730             File chunksFile = new File( dir, CHUNKS_FILENAME );
731             BufferedOutputStream os = new BufferedOutputStream( new FileOutputStream( chunksFile, true ) );
732             Writer w = new OutputStreamWriter( os, CHUNKS_FILE_ENCODING );
733             try
734             {
735                 for ( String filename : newChunks )
736                 {
737                     w.write( filename + "\n" );
738                 }
739                 w.flush();
740             }
741             finally
742             {
743                 IOUtil.close( w );
744                 IOUtil.close( os );
745             }
746             super.commit();
747         }
748 
749         public List<String> getChunks()
750             throws IOException
751         {
752             ArrayList<String> chunks = new ArrayList<String>();
753 
754             File chunksFile = new File( dir, CHUNKS_FILENAME );
755             BufferedReader r =
756                 new BufferedReader( new InputStreamReader( new FileInputStream( chunksFile ), CHUNKS_FILE_ENCODING ) );
757             try
758             {
759                 String str;
760                 while ( ( str = r.readLine() ) != null )
761                 {
762                     chunks.add( str );
763                 }
764             }
765             finally
766             {
767                 IOUtil.close( r );
768             }
769             return chunks;
770         }
771 
772         public ResourceFetcher getFetcher()
773         {
774             return new LocalIndexCacheFetcher( dir )
775             {
776                 @Override
777                 public List<String> getChunks()
778                     throws IOException
779                 {
780                     return LocalCacheIndexAdaptor.this.getChunks();
781                 }
782             };
783         }
784     }
785 
786     abstract static class LocalIndexCacheFetcher
787         extends FileFetcher
788     {
789         public LocalIndexCacheFetcher( File basedir )
790         {
791             super( basedir );
792         }
793 
794         public abstract List<String> getChunks()
795             throws IOException;
796     }
797 
798     private Date fetchAndUpdateIndex( final IndexUpdateRequest updateRequest, ResourceFetcher source,
799                                       IndexAdaptor target )
800         throws IOException
801     {
802         if ( !updateRequest.isForceFullUpdate() )
803         {
804             Properties localProperties = target.getProperties();
805             Date localTimestamp = null;
806 
807             if ( localProperties != null )
808             {
809                 localTimestamp = getTimestamp( localProperties, IndexingContext.INDEX_TIMESTAMP );
810             }
811 
812             // this will download and store properties in the target, so next run
813             // target.getProperties() will retrieve it
814             Properties remoteProperties = target.setProperties( source );
815 
816             Date updateTimestamp = getTimestamp( remoteProperties, IndexingContext.INDEX_TIMESTAMP );
817 
818             // If new timestamp is missing, dont bother checking incremental, we have an old file
819             if ( updateTimestamp != null )
820             {
821                 List<String> filenames =
822                     incrementalHandler.loadRemoteIncrementalUpdates( updateRequest, localProperties, remoteProperties );
823 
824                 // if we have some incremental files, merge them in
825                 if ( filenames != null )
826                 {
827                     for ( String filename : filenames )
828                     {
829                         target.addIndexChunk( source, filename );
830                     }
831 
832                     return updateTimestamp;
833                 }
834             }
835             else
836             {
837                 updateTimestamp = getTimestamp( remoteProperties, IndexingContext.INDEX_LEGACY_TIMESTAMP );
838             }
839 
840             // fallback to timestamp comparison, but try with one coming from local properties, and if not possible (is
841             // null)
842             // fallback to context timestamp
843             if ( localTimestamp != null )
844             {
845                 // if we have localTimestamp
846                 // if incremental can't be done for whatever reason, simply use old logic of
847                 // checking the timestamp, if the same, nothing to do
848                 if ( updateTimestamp != null && localTimestamp != null && !updateTimestamp.after( localTimestamp ) )
849                 {
850                     return null; // index is up to date
851                 }
852             }
853         }
854         else
855         {
856             // create index properties during forced full index download
857             target.setProperties( source );
858         }
859 
860         try
861         {
862             Date timestamp = target.setIndexFile( source, IndexingContext.INDEX_FILE_PREFIX + ".gz" );
863             if ( source instanceof LocalIndexCacheFetcher )
864             {
865                 // local cache has inverse organization compared to remote indexes,
866                 // i.e. initial index file and delta chunks to apply on top of it
867                 for ( String filename : ( (LocalIndexCacheFetcher) source ).getChunks() )
868                 {
869                     target.addIndexChunk( source, filename );
870                 }
871             }
872             return timestamp;
873         }
874         catch ( IOException ex )
875         {
876             // try to look for legacy index transfer format
877             try
878             {
879                 return target.setIndexFile( source, IndexingContext.INDEX_FILE_PREFIX + ".zip" );
880             }
881             catch ( IOException ex2 )
882             {
883                 getLogger().error( "Fallback to *.zip also failed: " + ex2 ); // do not bother with stack trace
884                 
885                 throw ex; // original exception more likely to be interesting
886             }
887         }
888     }
889 
890     /**
891      * Cleans specified cache directory. If present, Locker.LOCK_FILE will not be deleted.
892      */
893     protected void cleanCacheDirectory( File dir )
894         throws IOException
895     {
896         File[] members = dir.listFiles();
897         if ( members == null )
898         {
899             return;
900         }
901 
902         for ( File member : members )
903         {
904             if ( !Locker.LOCK_FILE.equals( member.getName() ) )
905             {
906                 FileUtils.forceDelete( member );
907             }
908         }
909     }
910 
911 }