1 package org.apache.maven.index.incremental;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 public class DefaultIncrementalHandler
50 extends AbstractLogEnabled
51 implements IncrementalHandler
52 {
53 public List<Integer> getIncrementalUpdates( IndexPackingRequest request, Properties properties )
54 throws IOException
55 {
56 getLogger().debug( "Handling Incremental Updates" );
57
58 if ( !validateProperties( properties ) )
59 {
60 getLogger().debug( "Invalid properties found, resetting them and doing no incremental packing." );
61 return null;
62 }
63
64
65
66 List<Integer> chunk =
67 getIndexChunk( request, parse( properties.getProperty( IndexingContext.INDEX_TIMESTAMP ) ) );
68
69 getLogger().debug( "Found " + chunk.size() + " differences to put in incremental index." );
70
71
72 if ( chunk.size() > 0 )
73 {
74 updateProperties( properties, request );
75 }
76
77 cleanUpIncrementalChunks( request, properties );
78
79 return chunk;
80 }
81
82 public List<String> loadRemoteIncrementalUpdates( IndexUpdateRequest request, Properties localProperties,
83 Properties remoteProperties )
84 throws IOException
85 {
86 List<String> filenames = null;
87
88 if ( canRetrieveAllChunks( localProperties, remoteProperties ) )
89 {
90 filenames = new ArrayList<String>();
91
92 int maxCounter = Integer.parseInt( remoteProperties.getProperty( IndexingContext.INDEX_CHUNK_COUNTER ) );
93 int currentCounter = Integer.parseInt( localProperties.getProperty( IndexingContext.INDEX_CHUNK_COUNTER ) );
94
95
96 currentCounter++;
97
98 while ( currentCounter <= maxCounter )
99 {
100 filenames.add( IndexingContext.INDEX_FILE_PREFIX + "." + currentCounter++ + ".gz" );
101 }
102 }
103
104 return filenames;
105 }
106
107 private boolean validateProperties( Properties properties )
108 {
109 if ( properties == null || properties.isEmpty() )
110 {
111 return false;
112 }
113
114 if ( properties.getProperty( IndexingContext.INDEX_TIMESTAMP ) == null )
115 {
116 return false;
117 }
118
119 if ( parse( properties.getProperty( IndexingContext.INDEX_TIMESTAMP ) ) == null )
120 {
121 return false;
122 }
123
124 initializeProperties( properties );
125
126 return true;
127 }
128
129 public void initializeProperties( Properties properties )
130 {
131 if ( properties.getProperty( IndexingContext.INDEX_CHAIN_ID ) == null )
132 {
133 properties.setProperty( IndexingContext.INDEX_CHAIN_ID, Long.toString( new Date().getTime() ) );
134 properties.remove( IndexingContext.INDEX_CHUNK_COUNTER );
135 }
136
137 if ( properties.getProperty( IndexingContext.INDEX_CHUNK_COUNTER ) == null )
138 {
139 properties.setProperty( IndexingContext.INDEX_CHUNK_COUNTER, "0" );
140 }
141 }
142
143
144 private List<Integer> getIndexChunk( IndexPackingRequest request, Date timestamp )
145 throws IOException
146 {
147 List<Integer> chunk = new ArrayList<Integer>();
148
149 IndexReader r = request.getContext().getIndexReader();
150
151 for ( int i = 0; i < r.maxDoc(); i++ )
152 {
153 if ( !r.isDeleted( i ) )
154 {
155 Document d = r.document( i );
156
157 String lastModified = d.get( ArtifactInfo.LAST_MODIFIED );
158
159 if ( lastModified != null )
160 {
161 Date t = new Date( Long.parseLong( lastModified ) );
162
163
164 if ( t.after( timestamp ) )
165 {
166 chunk.add( i );
167 }
168 }
169 }
170 }
171
172 return chunk;
173 }
174
175 private void updateProperties( Properties properties, IndexPackingRequest request )
176 throws IOException
177 {
178 Set<Object> keys = new HashSet<Object>( properties.keySet() );
179 Map<Integer, String> dataMap = new TreeMap<Integer, String>();
180
181
182 for ( Object key : keys )
183 {
184 String sKey = (String) key;
185
186 if ( sKey.startsWith( IndexingContext.INDEX_CHUNK_PREFIX ) )
187 {
188 Integer count = Integer.valueOf( sKey.substring( IndexingContext.INDEX_CHUNK_PREFIX.length() ) );
189 String value = properties.getProperty( sKey );
190
191 dataMap.put( count, value );
192 properties.remove( key );
193 }
194 }
195
196 String val = (String) properties.getProperty( IndexingContext.INDEX_CHUNK_COUNTER );
197
198 int i = 0;
199
200 for ( Entry<Integer, String> entry : dataMap.entrySet() )
201 {
202
203 if ( i >= ( request.getMaxIndexChunks() - 1 ) )
204 {
205 break;
206 }
207
208 properties.put( IndexingContext.INDEX_CHUNK_PREFIX + ( entry.getKey() + 1 ), entry.getValue() );
209
210 i++;
211 }
212
213 int nextValue = Integer.parseInt( val ) + 1;
214
215
216 properties.put( IndexingContext.INDEX_CHUNK_PREFIX + "0", Integer.toString( nextValue ) );
217 properties.put( IndexingContext.INDEX_CHUNK_COUNTER, Integer.toString( nextValue ) );
218 }
219
220 private void cleanUpIncrementalChunks( IndexPackingRequest request, Properties properties )
221 throws IOException
222 {
223 File[] files = request.getTargetDir().listFiles( new FilenameFilter()
224 {
225 public boolean accept( File dir, String name )
226 {
227 String[] parts = name.split( "\\." );
228
229 if ( parts.length == 3 && parts[0].equals( IndexingContext.INDEX_FILE_PREFIX ) && parts[2].equals(
230 "gz" ) )
231 {
232 return true;
233 }
234
235 return false;
236 }
237 } );
238
239 for ( int i = 0; i < files.length; i++ )
240 {
241 String[] parts = files[i].getName().split( "\\." );
242
243 boolean found = false;
244 for ( Entry<Object, Object> entry : properties.entrySet() )
245 {
246 if ( entry.getKey().toString().startsWith( IndexingContext.INDEX_CHUNK_PREFIX )
247 && entry.getValue().equals( parts[1] ) )
248 {
249 found = true;
250 break;
251 }
252 }
253
254 if ( !found )
255 {
256 files[i].delete();
257 }
258 }
259 }
260
261 private Date parse( String s )
262 {
263 try
264 {
265 SimpleDateFormat df = new SimpleDateFormat( IndexingContext.INDEX_TIME_FORMAT );
266 df.setTimeZone( TimeZone.getTimeZone( "GMT" ) );
267 return df.parse( s );
268 }
269 catch ( ParseException e )
270 {
271 return null;
272 }
273 }
274
275 private boolean canRetrieveAllChunks( Properties localProps, Properties remoteProps )
276 {
277
278 if ( localProps == null )
279 {
280 return false;
281 }
282
283 String localChainId = localProps.getProperty( IndexingContext.INDEX_CHAIN_ID );
284 String remoteChainId = remoteProps.getProperty( IndexingContext.INDEX_CHAIN_ID );
285
286
287 if ( StringUtils.isEmpty( localChainId ) || !localChainId.equals( remoteChainId ) )
288 {
289 return false;
290 }
291
292 String counterProp = localProps.getProperty( IndexingContext.INDEX_CHUNK_COUNTER );
293
294
295
296 if ( StringUtils.isEmpty( counterProp ) || !StringUtils.isNumeric( counterProp ) )
297 {
298 return false;
299 }
300
301 int currentLocalCounter = Integer.parseInt( counterProp );
302
303
304
305 for ( Object key : remoteProps.keySet() )
306 {
307 String sKey = (String) key;
308
309 if ( sKey.startsWith( IndexingContext.INDEX_CHUNK_PREFIX ) )
310 {
311 String value = remoteProps.getProperty( sKey );
312
313
314 if ( Integer.toString( currentLocalCounter ).equals( value ) || Integer.toString(
315 currentLocalCounter + 1 ).equals( value ) )
316 {
317 return true;
318 }
319 }
320 }
321
322 return false;
323 }
324 }