Coverage Report - org.apache.maven.index.incremental.DefaultIncrementalHandler
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultIncrementalHandler
98 %
98/100
92 %
59/64
5,6
DefaultIncrementalHandler$1
100 %
5/5
66 %
4/6
5,6
 
 1  
 package org.apache.maven.index.incremental;
 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 org.apache.lucene.document.Document;
 23  
 import org.apache.lucene.index.IndexReader;
 24  
 import org.apache.maven.index.ArtifactInfo;
 25  
 import org.apache.maven.index.context.IndexingContext;
 26  
 import org.apache.maven.index.packer.IndexPackingRequest;
 27  
 import org.apache.maven.index.updater.IndexUpdateRequest;
 28  
 import org.codehaus.plexus.component.annotations.Component;
 29  
 import org.codehaus.plexus.logging.AbstractLogEnabled;
 30  
 import org.codehaus.plexus.util.StringUtils;
 31  
 
 32  
 import java.io.File;
 33  
 import java.io.FilenameFilter;
 34  
 import java.io.IOException;
 35  
 import java.text.ParseException;
 36  
 import java.text.SimpleDateFormat;
 37  
 import java.util.ArrayList;
 38  
 import java.util.Date;
 39  
 import java.util.HashSet;
 40  
 import java.util.List;
 41  
 import java.util.Map;
 42  
 import java.util.Map.Entry;
 43  
 import java.util.Properties;
 44  
 import java.util.Set;
 45  
 import java.util.TimeZone;
 46  
 import java.util.TreeMap;
 47  
 
 48  
 @Component( role = IncrementalHandler.class )
 49  51
 public class DefaultIncrementalHandler
 50  
     extends AbstractLogEnabled
 51  
     implements IncrementalHandler
 52  
 {
 53  
     public List<Integer> getIncrementalUpdates( IndexPackingRequest request, Properties properties )
 54  
         throws IOException
 55  
     {
 56  26
         getLogger().debug( "Handling Incremental Updates" );
 57  
 
 58  26
         if ( !validateProperties( properties ) )
 59  
         {
 60  2
             getLogger().debug( "Invalid properties found, resetting them and doing no incremental packing." );
 61  2
             return null;
 62  
         }
 63  
 
 64  
         // Get the list of document ids that have been added since the last time
 65  
         // the index ran
 66  24
         List<Integer> chunk =
 67  
             getIndexChunk( request, parse( properties.getProperty( IndexingContext.INDEX_TIMESTAMP ) ) );
 68  
 
 69  24
         getLogger().debug( "Found " + chunk.size() + " differences to put in incremental index." );
 70  
 
 71  
         // if no documents, then we don't need to do anything, no changes
 72  24
         if ( chunk.size() > 0 )
 73  
         {
 74  23
             updateProperties( properties, request );
 75  
         }
 76  
 
 77  24
         cleanUpIncrementalChunks( request, properties );
 78  
 
 79  24
         return chunk;
 80  
     }
 81  
 
 82  
     public List<String> loadRemoteIncrementalUpdates( IndexUpdateRequest request, Properties localProperties,
 83  
                                                       Properties remoteProperties )
 84  
         throws IOException
 85  
     {
 86  31
         List<String> filenames = null;
 87  
         // If we have local properties, will parse and see what we need to download
 88  31
         if ( canRetrieveAllChunks( localProperties, remoteProperties ) )
 89  
         {
 90  7
             filenames = new ArrayList<String>();
 91  
 
 92  7
             int maxCounter = Integer.parseInt( remoteProperties.getProperty( IndexingContext.INDEX_CHUNK_COUNTER ) );
 93  7
             int currentCounter = Integer.parseInt( localProperties.getProperty( IndexingContext.INDEX_CHUNK_COUNTER ) );
 94  
 
 95  
             // Start with the next one
 96  7
             currentCounter++;
 97  
 
 98  12
             while ( currentCounter <= maxCounter )
 99  
             {
 100  5
                 filenames.add( IndexingContext.INDEX_FILE_PREFIX + "." + currentCounter++ + ".gz" );
 101  
             }
 102  
         }
 103  
 
 104  31
         return filenames;
 105  
     }
 106  
 
 107  
     private boolean validateProperties( Properties properties )
 108  
     {
 109  26
         if ( properties == null || properties.isEmpty() )
 110  
         {
 111  1
             return false;
 112  
         }
 113  
 
 114  25
         if ( properties.getProperty( IndexingContext.INDEX_TIMESTAMP ) == null )
 115  
         {
 116  0
             return false;
 117  
         }
 118  
 
 119  25
         if ( parse( properties.getProperty( IndexingContext.INDEX_TIMESTAMP ) ) == null )
 120  
         {
 121  1
             return false;
 122  
         }
 123  
 
 124  24
         initializeProperties( properties );
 125  
 
 126  24
         return true;
 127  
     }
 128  
 
 129  
     public void initializeProperties( Properties properties )
 130  
     {
 131  61
         if ( properties.getProperty( IndexingContext.INDEX_CHAIN_ID ) == null )
 132  
         {
 133  39
             properties.setProperty( IndexingContext.INDEX_CHAIN_ID, Long.toString( new Date().getTime() ) );
 134  39
             properties.remove( IndexingContext.INDEX_CHUNK_COUNTER );
 135  
         }
 136  
 
 137  61
         if ( properties.getProperty( IndexingContext.INDEX_CHUNK_COUNTER ) == null )
 138  
         {
 139  39
             properties.setProperty( IndexingContext.INDEX_CHUNK_COUNTER, "0" );
 140  
         }
 141  61
     }
 142  
 
 143  
     // Note Toni:
 144  
     private List<Integer> getIndexChunk( IndexPackingRequest request, Date timestamp )
 145  
         throws IOException
 146  
     {
 147  24
         List<Integer> chunk = new ArrayList<Integer>();
 148  
 
 149  24
         IndexReader r = request.getContext().getIndexReader();
 150  
 
 151  118
         for ( int i = 0; i < r.maxDoc(); i++ )
 152  
         {
 153  94
             if ( !r.isDeleted( i ) )
 154  
             {
 155  94
                 Document d = r.document( i );
 156  
 
 157  94
                 String lastModified = d.get( ArtifactInfo.LAST_MODIFIED );
 158  
 
 159  94
                 if ( lastModified != null )
 160  
                 {
 161  25
                     Date t = new Date( Long.parseLong( lastModified ) );
 162  
 
 163  
                     // Only add documents that were added after the last time we indexed
 164  25
                     if ( t.after( timestamp ) )
 165  
                     {
 166  23
                         chunk.add( i );
 167  
                     }
 168  
                 }
 169  
             }
 170  
         }
 171  
 
 172  24
         return chunk;
 173  
     }
 174  
 
 175  
     private void updateProperties( Properties properties, IndexPackingRequest request )
 176  
         throws IOException
 177  
     {
 178  23
         Set<Object> keys = new HashSet<Object>( properties.keySet() );
 179  23
         Map<Integer, String> dataMap = new TreeMap<Integer, String>();
 180  
 
 181  
         // First go through and retrieve all keys and their values
 182  23
         for ( Object key : keys )
 183  
         {
 184  133
             String sKey = (String) key;
 185  
 
 186  133
             if ( sKey.startsWith( IndexingContext.INDEX_CHUNK_PREFIX ) )
 187  
             {
 188  20
                 Integer count = Integer.valueOf( sKey.substring( IndexingContext.INDEX_CHUNK_PREFIX.length() ) );
 189  20
                 String value = properties.getProperty( sKey );
 190  
 
 191  20
                 dataMap.put( count, value );
 192  20
                 properties.remove( key );
 193  
             }
 194  133
         }
 195  
 
 196  23
         String val = (String) properties.getProperty( IndexingContext.INDEX_CHUNK_COUNTER );
 197  
 
 198  23
         int i = 0;
 199  
         // Next put the items back in w/ proper keys
 200  23
         for ( Entry<Integer, String> entry : dataMap.entrySet() )
 201  
         {
 202  
             // make sure to end if we reach limit, 0 based
 203  20
             if ( i >= ( request.getMaxIndexChunks() - 1 ) )
 204  
             {
 205  2
                 break;
 206  
             }
 207  
 
 208  18
             properties.put( IndexingContext.INDEX_CHUNK_PREFIX + ( entry.getKey() + 1 ), entry.getValue() );
 209  
 
 210  18
             i++;
 211  
         }
 212  
 
 213  23
         int nextValue = Integer.parseInt( val ) + 1;
 214  
 
 215  
         // Now put the new one in, and update the counter
 216  23
         properties.put( IndexingContext.INDEX_CHUNK_PREFIX + "0", Integer.toString( nextValue ) );
 217  23
         properties.put( IndexingContext.INDEX_CHUNK_COUNTER, Integer.toString( nextValue ) );
 218  23
     }
 219  
 
 220  
     private void cleanUpIncrementalChunks( IndexPackingRequest request, Properties properties )
 221  
         throws IOException
 222  
     {
 223  24
         File[] files = request.getTargetDir().listFiles( new FilenameFilter()
 224  24
         {
 225  
             public boolean accept( File dir, String name )
 226  
             {
 227  359
                 String[] parts = name.split( "\\." );
 228  
 
 229  359
                 if ( parts.length == 3 && parts[0].equals( IndexingContext.INDEX_FILE_PREFIX ) && parts[2].equals(
 230  
                     "gz" ) )
 231  
                 {
 232  20
                     return true;
 233  
                 }
 234  
 
 235  339
                 return false;
 236  
             }
 237  
         } );
 238  
 
 239  44
         for ( int i = 0; i < files.length; i++ )
 240  
         {
 241  20
             String[] parts = files[i].getName().split( "\\." );
 242  
 
 243  20
             boolean found = false;
 244  20
             for ( Entry<Object, Object> entry : properties.entrySet() )
 245  
             {
 246  64
                 if ( entry.getKey().toString().startsWith( IndexingContext.INDEX_CHUNK_PREFIX )
 247  
                     && entry.getValue().equals( parts[1] ) )
 248  
                 {
 249  18
                     found = true;
 250  18
                     break;
 251  
                 }
 252  
             }
 253  
 
 254  20
             if ( !found )
 255  
             {
 256  2
                 files[i].delete();
 257  
             }
 258  
         }
 259  24
     }
 260  
 
 261  
     private Date parse( String s )
 262  
     {
 263  
         try
 264  
         {
 265  49
             SimpleDateFormat df = new SimpleDateFormat( IndexingContext.INDEX_TIME_FORMAT );
 266  49
             df.setTimeZone( TimeZone.getTimeZone( "GMT" ) );
 267  49
             return df.parse( s );
 268  
         }
 269  1
         catch ( ParseException e )
 270  
         {
 271  1
             return null;
 272  
         }
 273  
     }
 274  
 
 275  
     private boolean canRetrieveAllChunks( Properties localProps, Properties remoteProps )
 276  
     {
 277  
         // no localprops, can't retrieve chunks
 278  31
         if ( localProps == null )
 279  
         {
 280  17
             return false;
 281  
         }
 282  
 
 283  14
         String localChainId = localProps.getProperty( IndexingContext.INDEX_CHAIN_ID );
 284  14
         String remoteChainId = remoteProps.getProperty( IndexingContext.INDEX_CHAIN_ID );
 285  
 
 286  
         // If no chain id, or not the same, do whole download
 287  14
         if ( StringUtils.isEmpty( localChainId ) || !localChainId.equals( remoteChainId ) )
 288  
         {
 289  2
             return false;
 290  
         }
 291  
 
 292  12
         String counterProp = localProps.getProperty( IndexingContext.INDEX_CHUNK_COUNTER );
 293  
 
 294  
         // no counter, cant retrieve chunks
 295  
         // not a number, cant retrieve chunks
 296  12
         if ( StringUtils.isEmpty( counterProp ) || !StringUtils.isNumeric( counterProp ) )
 297  
         {
 298  0
             return false;
 299  
         }
 300  
 
 301  12
         int currentLocalCounter = Integer.parseInt( counterProp );
 302  
 
 303  
         // check remote props for existence of next chunk after local
 304  
         // if we find it, then we are ok to retrieve the rest of the chunks
 305  12
         for ( Object key : remoteProps.keySet() )
 306  
         {
 307  58
             String sKey = (String) key;
 308  
 
 309  58
             if ( sKey.startsWith( IndexingContext.INDEX_CHUNK_PREFIX ) )
 310  
             {
 311  9
                 String value = remoteProps.getProperty( sKey );
 312  
 
 313  
                 // If we have the current counter, or the next counter, we are good to go
 314  9
                 if ( Integer.toString( currentLocalCounter ).equals( value ) || Integer.toString(
 315  
                     currentLocalCounter + 1 ).equals( value ) )
 316  
                 {
 317  7
                     return true;
 318  
                 }
 319  
             }
 320  51
         }
 321  
 
 322  5
         return false;
 323  
     }
 324  
 }