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.updater;
20  
21  import java.io.ByteArrayInputStream;
22  import java.io.ByteArrayOutputStream;
23  import java.io.File;
24  import java.io.FileInputStream;
25  import java.io.FileOutputStream;
26  import java.io.IOException;
27  import java.io.InputStream;
28  import java.text.SimpleDateFormat;
29  import java.util.Collection;
30  import java.util.Date;
31  import java.util.Properties;
32  import java.util.Set;
33  
34  import org.apache.lucene.index.Term;
35  import org.apache.lucene.search.Query;
36  import org.apache.lucene.search.TermQuery;
37  import org.apache.lucene.store.ByteBuffersDirectory;
38  import org.apache.lucene.store.Directory;
39  import org.apache.lucene.store.IOContext;
40  import org.apache.maven.index.ArtifactInfo;
41  import org.apache.maven.index.FlatSearchRequest;
42  import org.apache.maven.index.FlatSearchResponse;
43  import org.apache.maven.index.MAVEN;
44  import org.apache.maven.index.SearchType;
45  import org.apache.maven.index.context.DocumentFilter;
46  import org.apache.maven.index.context.IndexUtils;
47  import org.apache.maven.index.context.IndexingContext;
48  import org.jmock.Expectations;
49  import org.jmock.Mockery;
50  import org.jmock.api.Invocation;
51  import org.jmock.lib.action.ReturnValueAction;
52  import org.jmock.lib.action.VoidAction;
53  import org.junit.Ignore;
54  import org.junit.Test;
55  
56  import static org.junit.Assert.assertEquals;
57  import static org.junit.Assert.assertFalse;
58  import static org.junit.Assert.assertTrue;
59  
60  /**
61   * @author Eugene Kuleshov
62   */
63  public class DefaultIndexUpdaterTest extends AbstractIndexUpdaterTest {
64  
65      SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss.SSS Z");
66  
67      @Test
68      public void testReplaceIndex() throws Exception {
69          indexer.addArtifactToIndex(
70                  createArtifactContext(repositoryId, "commons-lang", "commons-lang", "2.2", null), context);
71  
72          Query q = indexer.constructQuery(MAVEN.ARTIFACT_ID, "commons-lang", SearchType.SCORED);
73  
74          FlatSearchResponse response1 = indexer.searchFlat(new FlatSearchRequest(q));
75          Collection<ArtifactInfo> content1 = response1.getResults();
76  
77          assertEquals(content1.toString(), 1, content1.size());
78  
79          // updated index
80  
81          Directory tempIndexDirectory = new ByteBuffersDirectory();
82  
83          IndexingContext tempContext = indexer.addIndexingContext(
84                  repositoryId + "temp", repositoryId, null, tempIndexDirectory, repositoryUrl, null, MIN_CREATORS);
85  
86          indexer.addArtifactToIndex(
87                  createArtifactContext(repositoryId, "commons-lang", "commons-lang", "2.3", null), tempContext);
88  
89          indexer.addArtifactToIndex(
90                  createArtifactContext(repositoryId, "commons-lang", "commons-lang", "2.4", null), tempContext);
91  
92          FlatSearchResponse response2 = indexer.searchFlat(new FlatSearchRequest(q, tempContext));
93          Collection<ArtifactInfo> tempContent = response2.getResults();
94          assertEquals(tempContent.toString(), 2, tempContent.size());
95  
96          // RAMDirectory is closed with context, forcing timestamp update
97          tempContext.updateTimestamp(true);
98  
99          // A change in RAMDirectory and Directory behavior in general: it will copy the Index files ONLY
100         // So we must make sure that timestamp file is transferred correctly.
101         ByteBuffersDirectory tempDir2 = new ByteBuffersDirectory();
102         IndexUtils.copyDirectory(tempContext.getIndexDirectory(), tempDir2);
103 
104         Date newIndexTimestamp = tempContext.getTimestamp();
105 
106         indexer.removeIndexingContext(tempContext, false);
107 
108         context.replace(tempDir2);
109 
110         assertEquals(newIndexTimestamp, context.getTimestamp());
111 
112         FlatSearchResponse response3 = indexer.searchFlat(new FlatSearchRequest(q));
113         Collection<ArtifactInfo> content2 = response3.getResults();
114         assertEquals(content2.toString(), 2, content2.size());
115     }
116 
117     @Test
118     public void testMergeIndex() throws Exception {
119         indexer.addArtifactToIndex(
120                 createArtifactContext(repositoryId, "commons-lang", "commons-lang", "2.2", null), context);
121 
122         Query q = indexer.constructQuery(MAVEN.ARTIFACT_ID, "commons-lang", SearchType.SCORED);
123 
124         {
125             FlatSearchResponse response1 = indexer.searchFlat(new FlatSearchRequest(q));
126             Collection<ArtifactInfo> content1 = response1.getResults();
127 
128             assertEquals(content1.toString(), 1, content1.size());
129         }
130 
131         // updated index
132 
133         {
134             Directory tempIndexDirectory = new ByteBuffersDirectory();
135 
136             IndexingContext tempContext = indexer.addIndexingContext(
137                     repositoryId + "temp", repositoryId, null, tempIndexDirectory, repositoryUrl, null, MIN_CREATORS);
138 
139             // indexer.addArtifactToIndex(
140             // createArtifactContext( repositoryId, "commons-lang", "commons-lang", "2.2", null ),
141             // tempContext );
142 
143             indexer.addArtifactToIndex(
144                     createArtifactContext(repositoryId, "commons-lang", "commons-lang", "2.3", null), tempContext);
145 
146             indexer.addArtifactToIndex(
147                     createArtifactContext(repositoryId, "commons-lang", "commons-lang", "2.4", null), tempContext);
148 
149             FlatSearchResponse tempResponse = indexer.searchFlat(new FlatSearchRequest(q));
150             Collection<ArtifactInfo> tempContent = tempResponse.getResults();
151             assertEquals(tempContent.toString(), 3, tempContent.size());
152 
153             ByteBuffersDirectory tempDir2 = new ByteBuffersDirectory();
154             for (String file : tempContext.getIndexDirectory().listAll()) {
155                 tempDir2.copyFrom(tempContext.getIndexDirectory(), file, file, IOContext.DEFAULT);
156             }
157 
158             indexer.removeIndexingContext(tempContext, false);
159 
160             context.merge(tempDir2);
161 
162             FlatSearchResponse response2 = indexer.searchFlat(new FlatSearchRequest(q));
163             Collection<ArtifactInfo> content2 = response2.getResults();
164             assertEquals(content2.toString(), 3, content2.size());
165         }
166     }
167 
168     @Test
169     public void testMergeIndexDeletes() throws Exception {
170         indexer.addArtifactToIndex(
171                 createArtifactContext(repositoryId, "commons-lang", "commons-lang", "2.2", null), context);
172 
173         indexer.addArtifactToIndex(
174                 createArtifactContext(repositoryId, "commons-lang", "commons-lang", "2.3", null), context);
175 
176         indexer.addArtifactToIndex(
177                 createArtifactContext(repositoryId, "commons-lang", "commons-lang", "2.4", null), context);
178 
179         {
180             Directory tempIndexDirectory = new ByteBuffersDirectory();
181 
182             IndexingContext tempContext = indexer.addIndexingContext(
183                     repositoryId + "temp", repositoryId, null, tempIndexDirectory, repositoryUrl, null, MIN_CREATORS);
184 
185             indexer.addArtifactToIndex(
186                     createArtifactContext(repositoryId, "commons-lang", "commons-lang", "2.4", null), tempContext);
187 
188             indexer.addArtifactToIndex(
189                     createArtifactContext(repositoryId, "commons-lang", "commons-lang", "2.2", null), tempContext);
190 
191             indexer.deleteArtifactFromIndex(
192                     createArtifactContext(repositoryId, "commons-lang", "commons-lang", "2.2", null), tempContext);
193 
194             indexer.deleteArtifactFromIndex(
195                     createArtifactContext(repositoryId, "commons-lang", "commons-lang", "2.4", null), tempContext);
196 
197             ByteBuffersDirectory tempDir2 = new ByteBuffersDirectory();
198             for (String file : tempContext.getIndexDirectory().listAll()) {
199                 tempDir2.copyFrom(tempContext.getIndexDirectory(), file, file, IOContext.DEFAULT);
200             }
201 
202             indexer.removeIndexingContext(tempContext, false);
203 
204             context.merge(tempDir2);
205         }
206 
207         Query q = indexer.constructQuery(MAVEN.ARTIFACT_ID, "commons-lang", SearchType.SCORED);
208 
209         FlatSearchResponse response = indexer.searchFlat(new FlatSearchRequest(q));
210         Collection<ArtifactInfo> content2 = response.getResults();
211 
212         assertEquals(content2.toString(), 1, content2.size());
213     }
214 
215     @Test
216     public void testMergeSearch() throws Exception {
217         File repo1 = new File(getBasedir(), "src/test/nexus-658");
218         Directory indexDir1 = new ByteBuffersDirectory();
219 
220         IndexingContext context1 =
221                 indexer.addIndexingContext("nexus-658", "nexus-658", repo1, indexDir1, null, null, DEFAULT_CREATORS);
222         indexer.scan(context1);
223 
224         File repo2 = new File(getBasedir(), "src/test/nexus-13");
225         Directory indexDir2 = new ByteBuffersDirectory();
226 
227         IndexingContext context2 =
228                 indexer.addIndexingContext("nexus-13", "nexus-13", repo2, indexDir2, null, null, DEFAULT_CREATORS);
229         indexer.scan(context2);
230 
231         context1.merge(indexDir2);
232 
233         Query q = new TermQuery(new Term(ArtifactInfo.SHA1, "b5e9d009320d11b9859c15d3ad3603b455fa1c85"));
234         FlatSearchRequest request = new FlatSearchRequest(q, context1);
235         FlatSearchResponse response = indexer.searchFlat(request);
236 
237         Set<ArtifactInfo> results = response.getResults();
238         ArtifactInfo artifactInfo = results.iterator().next();
239         assertEquals(artifactInfo.getArtifactId(), "dma.integration.tests");
240     }
241 
242     @Test
243     public void testMergeGroups() throws Exception {
244         indexer.addArtifactToIndex(
245                 createArtifactContext(repositoryId, "commons-lang", "commons-lang", "2.2", null), context);
246 
247         indexer.addArtifactToIndex(
248                 createArtifactContext(repositoryId, "commons-collections", "commons-collections", "1.0", null),
249                 context);
250 
251         indexer.addArtifactToIndex(
252                 createArtifactContext(repositoryId, "org.slf4j", "slf4j-api", "1.4.2", null), context);
253 
254         indexer.addArtifactToIndex(
255                 createArtifactContext(repositoryId, "org.slf4j", "slf4j-log4j12", "1.4.2", null), context);
256 
257         {
258             Directory tempIndexDirectory = new ByteBuffersDirectory();
259 
260             IndexingContext tempContext = indexer.addIndexingContext(
261                     repositoryId + "temp", repositoryId, null, tempIndexDirectory, repositoryUrl, null, MIN_CREATORS);
262 
263             indexer.addArtifactToIndex(
264                     createArtifactContext(repositoryId, "commons-lang", "commons-lang", "2.4", null), tempContext);
265 
266             indexer.addArtifactToIndex(createArtifactContext(repositoryId, "junit", "junit", "3.8", null), tempContext);
267 
268             indexer.addArtifactToIndex(
269                     createArtifactContext(repositoryId, "org.slf4j.foo", "jcl104-over-slf4j", "1.4.2", null), context);
270 
271             ByteBuffersDirectory tempDir2 = new ByteBuffersDirectory();
272             for (String file : tempContext.getIndexDirectory().listAll()) {
273                 tempDir2.copyFrom(tempContext.getIndexDirectory(), file, file, IOContext.DEFAULT);
274             }
275 
276             indexer.removeIndexingContext(tempContext, false);
277 
278             context.merge(tempDir2);
279         }
280 
281         Set<String> rootGroups = context.getRootGroups();
282 
283         assertEquals(rootGroups.toString(), 4, rootGroups.size());
284 
285         Set<String> allGroups = context.getAllGroups();
286 
287         assertEquals(allGroups.toString(), 5, allGroups.size());
288     }
289 
290     @Test
291     public void testNoIndexUpdate() throws Exception {
292         Mockery mockery = new Mockery();
293 
294         final String indexUrl = repositoryUrl + ".index";
295         final Date contextTimestamp = df.parse("20081125010000.000 -0600");
296 
297         final ResourceFetcher mockFetcher = mockery.mock(ResourceFetcher.class);
298 
299         final IndexingContext tempContext = mockery.mock(IndexingContext.class);
300 
301         final Properties localProps = new Properties();
302         localProps.setProperty(IndexingContext.INDEX_CHUNK_COUNTER, "1");
303         localProps.setProperty(IndexingContext.INDEX_CHAIN_ID, "someid");
304         localProps.setProperty(IndexingContext.INDEX_TIMESTAMP, "20081125010000.000 -0600");
305 
306         mockery.checking(new Expectations() {
307             {
308                 allowing(tempContext).getIndexDirectoryFile();
309                 will(new IndexDirectoryFileAction(localProps, testBasedir));
310 
311                 allowing(tempContext).getTimestamp();
312                 will(returnValue(contextTimestamp));
313 
314                 allowing(tempContext).getId();
315                 will(returnValue(repositoryId));
316 
317                 allowing(tempContext).commit();
318 
319                 allowing(tempContext).getIndexUpdateUrl();
320                 will(returnValue(indexUrl));
321 
322                 allowing(tempContext).getIndexCreators();
323                 will(returnValue(DEFAULT_CREATORS));
324 
325                 oneOf(mockFetcher).connect(repositoryId, indexUrl);
326 
327                 oneOf(mockFetcher)
328                         .retrieve( //
329                                 with(IndexingContext.INDEX_REMOTE_PROPERTIES_FILE));
330                 will(new PropertiesAction() {
331                     @Override
332                     Properties getProperties() {
333                         Properties properties = new Properties();
334                         properties.setProperty(IndexingContext.INDEX_ID, "central");
335                         properties.setProperty(IndexingContext.INDEX_TIMESTAMP, "20081125010000.000 -0600");
336                         return properties;
337                     }
338                 });
339 
340                 allowing(tempContext).getIndexDirectoryFile();
341 
342                 oneOf(mockFetcher).disconnect();
343             }
344         });
345 
346         // tempContext.updateTimestamp( true, contextTimestamp );
347 
348         IndexUpdateRequest updateRequest = new IndexUpdateRequest(tempContext, mockFetcher);
349 
350         IndexUpdateResult updateResult = updater.fetchAndUpdateIndex(updateRequest);
351 
352         mockery.assertIsSatisfied();
353         assertIndexUpdateSucceeded(updateResult);
354     }
355 
356     @Test
357     public void testFullIndexUpdate() throws Exception {
358         Mockery mockery = new Mockery();
359 
360         final String indexUrl = repositoryUrl + ".index";
361         final Date contextTimestamp = df.parse("20081125010000.000 -0600");
362 
363         final ResourceFetcher mockFetcher = mockery.mock(ResourceFetcher.class);
364 
365         final IndexingContext tempContext = mockery.mock(IndexingContext.class);
366 
367         mockery.checking(new Expectations() {
368             {
369                 allowing(tempContext).getIndexDirectoryFile();
370                 will(new ReturnValueAction(testBasedir));
371 
372                 allowing(tempContext).getTimestamp();
373                 will(returnValue(contextTimestamp));
374 
375                 allowing(tempContext).getId();
376                 will(returnValue(repositoryId));
377 
378                 allowing(tempContext).getIndexUpdateUrl();
379                 will(returnValue(indexUrl));
380 
381                 allowing(tempContext).commit();
382 
383                 allowing(tempContext).getIndexCreators();
384                 will(returnValue(DEFAULT_CREATORS));
385 
386                 allowing(tempContext).commit();
387 
388                 oneOf(mockFetcher).connect(repositoryId, indexUrl);
389 
390                 oneOf(mockFetcher)
391                         .retrieve( //
392                                 with(IndexingContext.INDEX_REMOTE_PROPERTIES_FILE));
393                 will(new PropertiesAction() {
394                     @Override
395                     Properties getProperties() {
396                         Properties properties = new Properties();
397                         properties.setProperty(IndexingContext.INDEX_ID, "central");
398                         properties.setProperty(IndexingContext.INDEX_TIMESTAMP, "20081126010000.000 -0600");
399                         return properties;
400                     }
401                 });
402 
403                 allowing(tempContext).getIndexDirectoryFile();
404 
405                 oneOf(mockFetcher)
406                         .retrieve( //
407                                 with(IndexingContext.INDEX_FILE_PREFIX + ".gz"));
408                 will(returnValue(newInputStream("index-updater/server-root/nexus-maven-repository-index.gz")));
409 
410                 oneOf(tempContext).replace(with(any(Directory.class)), with(any(Set.class)), with(any(Set.class)));
411 
412                 oneOf(mockFetcher).disconnect();
413             }
414         });
415 
416         // tempContext.updateTimestamp( true, contextTimestamp );
417 
418         IndexUpdateRequest updateRequest = new IndexUpdateRequest(tempContext, mockFetcher);
419 
420         IndexUpdateResult updateResult = updater.fetchAndUpdateIndex(updateRequest);
421 
422         mockery.assertIsSatisfied();
423         assertIndexUpdateSucceeded(updateResult);
424     }
425 
426     @Test
427     public void testIncrementalIndexUpdate() throws Exception {
428         Mockery mockery = new Mockery();
429 
430         final String indexUrl = repositoryUrl + ".index";
431         final Date contextTimestamp = df.parse("20081128000000.000 -0600");
432 
433         final ResourceFetcher mockFetcher = mockery.mock(ResourceFetcher.class);
434 
435         final IndexingContext tempContext = mockery.mock(IndexingContext.class);
436 
437         final Properties localProps = new Properties();
438         localProps.setProperty(IndexingContext.INDEX_CHUNK_COUNTER, "1");
439         localProps.setProperty(IndexingContext.INDEX_CHAIN_ID, "someid");
440 
441         mockery.checking(new Expectations() {
442             {
443                 allowing(tempContext).getTimestamp();
444                 will(returnValue(contextTimestamp));
445 
446                 allowing(tempContext).getId();
447                 will(returnValue(repositoryId));
448 
449                 allowing(tempContext).getIndexUpdateUrl();
450                 will(returnValue(indexUrl));
451 
452                 allowing(tempContext).commit();
453 
454                 allowing(tempContext).getIndexCreators();
455                 will(returnValue(DEFAULT_CREATORS));
456 
457                 oneOf(mockFetcher).connect(repositoryId, indexUrl);
458 
459                 oneOf(mockFetcher)
460                         .retrieve( //
461                                 with(IndexingContext.INDEX_REMOTE_PROPERTIES_FILE));
462                 will(new PropertiesAction() {
463                     @Override
464                     Properties getProperties() {
465                         Properties properties = new Properties();
466                         properties.setProperty(IndexingContext.INDEX_ID, "central");
467                         properties.setProperty(IndexingContext.INDEX_TIMESTAMP, "20081129174241.859 -0600");
468                         properties.setProperty(IndexingContext.INDEX_CHUNK_COUNTER, "3");
469                         properties.setProperty(IndexingContext.INDEX_CHAIN_ID, "someid");
470                         properties.setProperty(IndexingContext.INDEX_CHUNK_PREFIX + "0", "3");
471                         properties.setProperty(IndexingContext.INDEX_CHUNK_PREFIX + "1", "2");
472                         properties.setProperty(IndexingContext.INDEX_CHUNK_PREFIX + "2", "1");
473                         return properties;
474                     }
475                 });
476 
477                 allowing(tempContext).getIndexDirectoryFile();
478                 will(new IndexDirectoryFileAction(localProps, testBasedir));
479 
480                 oneOf(mockFetcher)
481                         .retrieve( //
482                                 with(IndexingContext.INDEX_FILE_PREFIX + ".2.gz"));
483                 will(returnValue(newInputStream("index-updater/server-root/nexus-maven-repository-index.gz")));
484                 oneOf(mockFetcher)
485                         .retrieve( //
486                                 with(IndexingContext.INDEX_FILE_PREFIX + ".3.gz"));
487                 will(returnValue(newInputStream("index-updater/server-root/nexus-maven-repository-index.gz")));
488                 // could create index archive there and verify that it is merged correctly
489 
490                 oneOf(tempContext)
491                         .merge(
492                                 with(any(Directory.class)),
493                                 with(aNull(DocumentFilter.class)),
494                                 with(any(Set.class)),
495                                 with(any(Set.class)));
496 
497                 oneOf(tempContext)
498                         .merge(
499                                 with(any(Directory.class)),
500                                 with(aNull(DocumentFilter.class)),
501                                 with(any(Set.class)),
502                                 with(any(Set.class)));
503 
504                 oneOf(mockFetcher).disconnect();
505             }
506         });
507 
508         // tempContext.updateTimestamp( true, contextTimestamp );
509 
510         IndexUpdateRequest updateRequest = new IndexUpdateRequest(tempContext, mockFetcher);
511         updateRequest.setIncrementalOnly(true);
512 
513         IndexUpdateResult updateResult = updater.fetchAndUpdateIndex(updateRequest);
514 
515         mockery.assertIsSatisfied();
516         assertIndexUpdateSucceeded(updateResult);
517     }
518 
519     @Test
520     public void testIncrementalIndexUpdateNoCounter() throws Exception {
521         Mockery mockery = new Mockery();
522 
523         final String indexUrl = repositoryUrl + ".index";
524         final Date contextTimestamp = df.parse("20081128000000.000 -0600");
525 
526         final ResourceFetcher mockFetcher = mockery.mock(ResourceFetcher.class);
527 
528         final IndexingContext tempContext = mockery.mock(IndexingContext.class);
529 
530         mockery.checking(new Expectations() {
531             {
532                 allowing(tempContext).getIndexDirectoryFile();
533                 will(new ReturnValueAction(testBasedir));
534 
535                 allowing(tempContext).getTimestamp();
536                 will(returnValue(contextTimestamp));
537 
538                 allowing(tempContext).getId();
539                 will(returnValue(repositoryId));
540 
541                 allowing(tempContext).getIndexUpdateUrl();
542                 will(returnValue(indexUrl));
543 
544                 allowing(tempContext).commit();
545 
546                 allowing(tempContext).getIndexCreators();
547                 will(returnValue(DEFAULT_CREATORS));
548 
549                 oneOf(mockFetcher).connect(repositoryId, indexUrl);
550 
551                 oneOf(mockFetcher)
552                         .retrieve( //
553                                 with(IndexingContext.INDEX_REMOTE_PROPERTIES_FILE));
554                 will(new PropertiesAction() {
555                     @Override
556                     Properties getProperties() {
557                         Properties properties = new Properties();
558                         properties.setProperty(IndexingContext.INDEX_ID, "central");
559                         properties.setProperty(IndexingContext.INDEX_TIMESTAMP, "20081129174241.859 -0600");
560                         properties.setProperty(IndexingContext.INDEX_CHUNK_COUNTER, "3");
561                         properties.setProperty(IndexingContext.INDEX_CHAIN_ID, "someid");
562                         properties.setProperty(IndexingContext.INDEX_CHUNK_PREFIX + "0", "3");
563                         properties.setProperty(IndexingContext.INDEX_CHUNK_PREFIX + "1", "2");
564                         properties.setProperty(IndexingContext.INDEX_CHUNK_PREFIX + "2", "1");
565                         return properties;
566                     }
567                 });
568 
569                 oneOf(mockFetcher)
570                         .retrieve( //
571                                 with(IndexingContext.INDEX_FILE_PREFIX + ".gz"));
572                 will(returnValue(newInputStream("index-updater/server-root/nexus-maven-repository-index.gz")));
573                 // could create index archive there and verify that it is merged correctly
574 
575                 oneOf(tempContext).replace(with(any(Directory.class)), with(any(Set.class)), with(any(Set.class)));
576 
577                 never(mockFetcher)
578                         .retrieve( //
579                                 with(IndexingContext.INDEX_FILE_PREFIX + ".2.gz"));
580 
581                 never(tempContext).merge(with(any(Directory.class)));
582 
583                 oneOf(mockFetcher).disconnect();
584             }
585         });
586 
587         // tempContext.updateTimestamp( true, contextTimestamp );
588 
589         IndexUpdateRequest updateRequest = new IndexUpdateRequest(tempContext, mockFetcher);
590 
591         IndexUpdateResult updateResult = updater.fetchAndUpdateIndex(updateRequest);
592 
593         mockery.assertIsSatisfied();
594         assertIndexUpdateSucceeded(updateResult);
595     }
596 
597     @Test
598     public void testIncrementalOnlyIndexUpdateNoCounter() throws Exception {
599         Mockery mockery = new Mockery();
600 
601         final String indexUrl = repositoryUrl + ".index";
602         final Date contextTimestamp = df.parse("20081128000000.000 -0600");
603 
604         final ResourceFetcher mockFetcher = mockery.mock(ResourceFetcher.class);
605 
606         final IndexingContext tempContext = mockery.mock(IndexingContext.class);
607 
608         mockery.checking(new Expectations() {
609             {
610                 allowing(tempContext).getIndexDirectoryFile();
611                 will(new ReturnValueAction(testBasedir));
612 
613                 allowing(tempContext).getTimestamp();
614                 will(returnValue(contextTimestamp));
615 
616                 allowing(tempContext).getId();
617                 will(returnValue(repositoryId));
618 
619                 allowing(tempContext).getIndexUpdateUrl();
620                 will(returnValue(indexUrl));
621 
622                 allowing(tempContext).getIndexCreators();
623                 will(returnValue(DEFAULT_CREATORS));
624 
625                 oneOf(mockFetcher).connect(repositoryId, indexUrl);
626 
627                 oneOf(mockFetcher)
628                         .retrieve( //
629                                 with(IndexingContext.INDEX_REMOTE_PROPERTIES_FILE));
630                 will(new PropertiesAction() {
631                     @Override
632                     Properties getProperties() {
633                         Properties properties = new Properties();
634                         properties.setProperty(IndexingContext.INDEX_ID, "central");
635                         properties.setProperty(IndexingContext.INDEX_TIMESTAMP, "20081129174241.859 -0600");
636                         properties.setProperty(IndexingContext.INDEX_CHUNK_COUNTER, "3");
637                         properties.setProperty(IndexingContext.INDEX_CHAIN_ID, "someid");
638                         properties.setProperty(IndexingContext.INDEX_CHUNK_PREFIX + "0", "3");
639                         properties.setProperty(IndexingContext.INDEX_CHUNK_PREFIX + "1", "2");
640                         properties.setProperty(IndexingContext.INDEX_CHUNK_PREFIX + "2", "1");
641                         return properties;
642                     }
643                 });
644 
645                 oneOf(mockFetcher).disconnect();
646             }
647         });
648 
649         IndexUpdateRequest updateRequest = new IndexUpdateRequest(tempContext, mockFetcher);
650         updateRequest.setIncrementalOnly(true);
651 
652         IndexUpdateResult updateResult = updater.fetchAndUpdateIndex(updateRequest);
653 
654         mockery.assertIsSatisfied();
655         assertIndexUpdateFailed(updateResult);
656     }
657 
658     @Test
659     public void testIncrementalIndexUpdateNoUpdateNecessary() throws Exception {
660         Mockery mockery = new Mockery();
661 
662         final String indexUrl = repositoryUrl + ".index";
663         final Date contextTimestamp = df.parse("20081128000000.000 -0600");
664 
665         final ResourceFetcher mockFetcher = mockery.mock(ResourceFetcher.class);
666 
667         final IndexingContext tempContext = mockery.mock(IndexingContext.class);
668 
669         final Properties localProps = new Properties();
670         localProps.setProperty(IndexingContext.INDEX_CHUNK_COUNTER, "3");
671         localProps.setProperty(IndexingContext.INDEX_CHAIN_ID, "someid");
672 
673         mockery.checking(new Expectations() {
674             {
675                 allowing(tempContext).getTimestamp();
676                 will(returnValue(contextTimestamp));
677 
678                 allowing(tempContext).getId();
679                 will(returnValue(repositoryId));
680 
681                 allowing(tempContext).getIndexUpdateUrl();
682                 will(returnValue(indexUrl));
683 
684                 allowing(tempContext).getIndexCreators();
685                 will(returnValue(DEFAULT_CREATORS));
686 
687                 allowing(tempContext).commit();
688 
689                 oneOf(mockFetcher).connect(repositoryId, indexUrl);
690 
691                 oneOf(mockFetcher)
692                         .retrieve( //
693                                 with(IndexingContext.INDEX_REMOTE_PROPERTIES_FILE));
694                 will(new PropertiesAction() {
695                     @Override
696                     Properties getProperties() {
697                         Properties properties = new Properties();
698                         properties.setProperty(IndexingContext.INDEX_ID, "central");
699                         properties.setProperty(IndexingContext.INDEX_TIMESTAMP, "20081129174241.859 -0600");
700                         properties.setProperty(IndexingContext.INDEX_CHUNK_COUNTER, "3");
701                         properties.setProperty(IndexingContext.INDEX_CHAIN_ID, "someid");
702                         properties.setProperty(IndexingContext.INDEX_CHUNK_PREFIX + "0", "3");
703                         properties.setProperty(IndexingContext.INDEX_CHUNK_PREFIX + "1", "2");
704                         properties.setProperty(IndexingContext.INDEX_CHUNK_PREFIX + "2", "1");
705                         return properties;
706                     }
707                 });
708 
709                 allowing(tempContext).getIndexDirectoryFile();
710                 will(new IndexDirectoryFileAction(localProps, testBasedir));
711 
712                 never(mockFetcher)
713                         .retrieve( //
714                                 with(IndexingContext.INDEX_FILE_PREFIX + ".gz"));
715                 // could create index archive there and verify that it is merged correctly
716 
717                 never(mockFetcher)
718                         .retrieve( //
719                                 with(IndexingContext.INDEX_FILE_PREFIX + ".1.gz"));
720 
721                 never(mockFetcher)
722                         .retrieve( //
723                                 with(IndexingContext.INDEX_FILE_PREFIX + ".2.gz"));
724 
725                 never(mockFetcher)
726                         .retrieve( //
727                                 with(IndexingContext.INDEX_FILE_PREFIX + ".3.gz"));
728 
729                 never(tempContext).merge(with(any(Directory.class)));
730 
731                 never(tempContext).replace(with(any(Directory.class)));
732 
733                 oneOf(mockFetcher).disconnect();
734             }
735         });
736 
737         // tempContext.updateTimestamp( true, contextTimestamp );
738 
739         IndexUpdateRequest updateRequest = new IndexUpdateRequest(tempContext, mockFetcher);
740 
741         IndexUpdateResult updateResult = updater.fetchAndUpdateIndex(updateRequest);
742 
743         mockery.assertIsSatisfied();
744         assertIndexUpdateSucceeded(updateResult);
745     }
746 
747     @Test
748     public void testUpdateForceFullUpdate() throws Exception {
749         Mockery mockery = new Mockery();
750 
751         final String indexUrl = repositoryUrl + ".index";
752         final Date contextTimestamp = df.parse("20081128000000.000 -0600");
753 
754         final ResourceFetcher mockFetcher = mockery.mock(ResourceFetcher.class);
755 
756         final IndexingContext tempContext = mockery.mock(IndexingContext.class);
757 
758         mockery.checking(new Expectations() {
759             {
760                 allowing(tempContext).getIndexDirectoryFile();
761                 will(new ReturnValueAction(testBasedir));
762 
763                 allowing(tempContext).getTimestamp();
764                 will(returnValue(contextTimestamp));
765 
766                 allowing(tempContext).getId();
767                 will(returnValue(repositoryId));
768 
769                 allowing(tempContext).getIndexUpdateUrl();
770                 will(returnValue(indexUrl));
771 
772                 allowing(tempContext).commit();
773 
774                 allowing(tempContext).getIndexCreators();
775                 will(returnValue(DEFAULT_CREATORS));
776 
777                 oneOf(mockFetcher).connect(repositoryId, indexUrl);
778 
779                 oneOf(mockFetcher)
780                         .retrieve( //
781                                 with(IndexingContext.INDEX_REMOTE_PROPERTIES_FILE));
782                 will(new PropertiesAction() {
783                     @Override
784                     Properties getProperties() {
785                         Properties properties = new Properties();
786                         properties.setProperty(IndexingContext.INDEX_ID, "central");
787                         properties.setProperty(IndexingContext.INDEX_TIMESTAMP, "20081129174241.859 -0600");
788                         properties.setProperty(IndexingContext.INDEX_CHUNK_COUNTER, "3");
789                         properties.setProperty(IndexingContext.INDEX_CHAIN_ID, "someid");
790                         properties.setProperty(IndexingContext.INDEX_CHUNK_PREFIX + "0", "3");
791                         properties.setProperty(IndexingContext.INDEX_CHUNK_PREFIX + "1", "2");
792                         properties.setProperty(IndexingContext.INDEX_CHUNK_PREFIX + "2", "1");
793                         return properties;
794                     }
795                 });
796 
797                 never(tempContext).getIndexDirectoryFile();
798 
799                 never(mockFetcher)
800                         .retrieve( //
801                                 with(IndexingContext.INDEX_FILE_PREFIX + ".1.gz"));
802 
803                 never(mockFetcher)
804                         .retrieve( //
805                                 with(IndexingContext.INDEX_FILE_PREFIX + ".2.gz"));
806 
807                 never(mockFetcher)
808                         .retrieve( //
809                                 with(IndexingContext.INDEX_FILE_PREFIX + ".3.gz"));
810 
811                 oneOf(mockFetcher).retrieve(with(IndexingContext.INDEX_FILE_PREFIX + ".gz"));
812                 will(returnValue(newInputStream("index-updater/server-root/nexus-maven-repository-index.gz")));
813 
814                 never(tempContext).merge(with(any(Directory.class)));
815 
816                 never(tempContext).merge(with(any(Directory.class)));
817 
818                 oneOf(tempContext).replace(with(any(Directory.class)), with(any(Set.class)), with(any(Set.class)));
819 
820                 oneOf(mockFetcher).disconnect();
821             }
822         });
823 
824         // tempContext.updateTimestamp( true, contextTimestamp );
825 
826         IndexUpdateRequest updateRequest = new IndexUpdateRequest(tempContext, mockFetcher);
827 
828         updateRequest.setForceFullUpdate(true);
829 
830         IndexUpdateResult updateResult = updater.fetchAndUpdateIndex(updateRequest);
831 
832         mockery.assertIsSatisfied();
833         assertIndexUpdateSucceeded(updateResult);
834     }
835 
836     @Test
837     @Ignore("Legacy format no longer supported with Lucene 4")
838     public void ignoreTestUpdateForceFullUpdateNoGZ() throws Exception {
839         Mockery mockery = new Mockery();
840 
841         final String indexUrl = repositoryUrl + ".index";
842         final Date contextTimestamp = df.parse("20081128000000.000 -0600");
843 
844         final ResourceFetcher mockFetcher = mockery.mock(ResourceFetcher.class);
845 
846         final IndexingContext tempContext = mockery.mock(IndexingContext.class);
847 
848         mockery.checking(new Expectations() {
849             {
850                 allowing(tempContext).getIndexDirectoryFile();
851                 will(new ReturnValueAction(testBasedir));
852 
853                 allowing(tempContext).getTimestamp();
854                 will(returnValue(contextTimestamp));
855 
856                 allowing(tempContext).commit();
857 
858                 allowing(tempContext).getId();
859                 will(returnValue(repositoryId));
860 
861                 allowing(tempContext).getIndexUpdateUrl();
862                 will(returnValue(indexUrl));
863 
864                 allowing(tempContext).getIndexCreators();
865                 will(returnValue(DEFAULT_CREATORS));
866 
867                 oneOf(mockFetcher).connect(repositoryId, indexUrl);
868 
869                 oneOf(mockFetcher)
870                         .retrieve( //
871                                 with(IndexingContext.INDEX_REMOTE_PROPERTIES_FILE));
872                 will(new PropertiesAction() {
873                     @Override
874                     Properties getProperties() {
875                         Properties properties = new Properties();
876                         properties.setProperty(IndexingContext.INDEX_ID, "central");
877                         properties.setProperty(IndexingContext.INDEX_LEGACY_TIMESTAMP, "20081129174241.859 -0600");
878                         return properties;
879                     }
880                 });
881 
882                 never(tempContext).getIndexDirectoryFile();
883 
884                 oneOf(mockFetcher).retrieve(with(IndexingContext.INDEX_FILE_PREFIX + ".gz"));
885 
886                 will(throwException(new IOException()));
887 
888                 oneOf(mockFetcher).retrieve(with(IndexingContext.INDEX_FILE_PREFIX + ".zip"));
889 
890                 will(returnValue(newInputStream("index-updater/server-root/legacy/nexus-maven-repository-index.zip")));
891 
892                 never(tempContext).merge(with(any(Directory.class)));
893 
894                 never(tempContext).merge(with(any(Directory.class)));
895 
896                 oneOf(tempContext).replace(with(any(Directory.class)));
897 
898                 oneOf(mockFetcher).disconnect();
899             }
900         });
901 
902         // tempContext.updateTimestamp( true, contextTimestamp );
903 
904         IndexUpdateRequest updateRequest = new IndexUpdateRequest(tempContext, mockFetcher);
905 
906         updateRequest.setForceFullUpdate(true);
907 
908         IndexUpdateResult updateResult = updater.fetchAndUpdateIndex(updateRequest);
909 
910         mockery.assertIsSatisfied();
911         assertIndexUpdateSucceeded(updateResult);
912     }
913 
914     protected InputStream newInputStream(String path) throws IOException {
915         File file = getTestFile("src/test/resources/" + path);
916         if (file.isFile()) {
917             return new FileInputStream(file);
918         }
919         return null;
920     }
921 
922     abstract static class PropertiesAction extends VoidAction {
923         @Override
924         public Object invoke(Invocation invocation) throws Throwable {
925             Properties properties = getProperties();
926 
927             try (ByteArrayOutputStream buf = new ByteArrayOutputStream()) {
928                 properties.store(buf, null);
929                 buf.flush();
930                 return new ByteArrayInputStream(buf.toByteArray());
931             }
932         }
933 
934         abstract Properties getProperties();
935     }
936 
937     private static class IndexDirectoryFileAction extends VoidAction {
938         File file;
939 
940         public IndexDirectoryFileAction(Properties properties, File basedir) throws Exception {
941             basedir.mkdirs();
942 
943             this.file = new File(basedir, IndexingContext.INDEX_UPDATER_PROPERTIES_FILE);
944 
945             try (FileOutputStream fos = new FileOutputStream(this.file)) {
946                 properties.store(fos, "");
947             }
948         }
949 
950         @Override
951         public Object invoke(Invocation invocation) throws Throwable {
952             return this.file.getParentFile();
953         }
954     }
955 
956     private void assertIndexUpdateSucceeded(IndexUpdateResult updateResult) {
957         assertTrue("Index update should have succeeded, but says it failed", updateResult.isSuccessful());
958     }
959 
960     private void assertIndexUpdateFailed(IndexUpdateResult updateResult) {
961         assertFalse("Index update should have failed, but says it succeeded", updateResult.isSuccessful());
962     }
963 }