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;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Singleton;
24  
25  import java.io.File;
26  import java.io.IOException;
27  import java.nio.file.Files;
28  import java.util.Collection;
29  import java.util.Collections;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.concurrent.ConcurrentHashMap;
33  
34  import org.apache.lucene.queryparser.classic.ParseException;
35  import org.apache.lucene.search.Query;
36  import org.apache.lucene.store.Directory;
37  import org.apache.lucene.store.FSDirectory;
38  import org.apache.maven.index.context.ContextMemberProvider;
39  import org.apache.maven.index.context.DefaultIndexingContext;
40  import org.apache.maven.index.context.ExistingLuceneIndexMismatchException;
41  import org.apache.maven.index.context.IndexCreator;
42  import org.apache.maven.index.context.IndexUtils;
43  import org.apache.maven.index.context.IndexingContext;
44  import org.apache.maven.index.context.MergedIndexingContext;
45  import org.apache.maven.index.context.StaticContextMemberProvider;
46  import org.apache.maven.index.context.UnsupportedExistingLuceneIndexException;
47  import org.apache.maven.index.expr.SearchExpression;
48  import org.apache.maven.index.util.IndexCreatorSorter;
49  import org.codehaus.plexus.util.FileUtils;
50  
51  /**
52   * A default {@link NexusIndexer} implementation.
53   *
54   * @author Tamas Cservenak
55   * @author Eugene Kuleshov
56   * @deprecated Use {@link Indexer} instead. Discouraged from further use, as it suffers from multiple synchronization
57   *             and other problems. As it tries to serve as "context registry" too, but it does not synchronize the
58   *             access to it, but also, introduces some extras (like "targeted" vs "non targeted" search), that makes
59   *             it's behavior less intuitive.
60   */
61  @Deprecated
62  @Singleton
63  @Named
64  public class DefaultNexusIndexer implements NexusIndexer {
65  
66      private final Indexer indexer;
67  
68      private final Scanner scanner;
69  
70      private final IndexerEngine indexerEngine;
71  
72      private final QueryCreator queryCreator;
73  
74      private final Map<String, IndexingContext> indexingContexts = new ConcurrentHashMap<>();
75  
76      @Inject
77      public DefaultNexusIndexer(
78              Indexer indexer, Scanner scanner, IndexerEngine indexerEngine, QueryCreator queryCreator) {
79          this.indexer = indexer;
80          this.scanner = scanner;
81          this.indexerEngine = indexerEngine;
82          this.queryCreator = queryCreator;
83      }
84  
85      // ----------------------------------------------------------------------------
86      // Contexts
87      // ----------------------------------------------------------------------------
88  
89      public void addIndexingContext(IndexingContext context) {
90          indexingContexts.put(context.getId(), context);
91      }
92  
93      public IndexingContext addIndexingContext(
94              String id,
95              String repositoryId,
96              File repository,
97              File indexDirectory,
98              String repositoryUrl,
99              String indexUpdateUrl,
100             List<? extends IndexCreator> indexers)
101             throws IOException, UnsupportedExistingLuceneIndexException {
102         try {
103             IndexingContext context = indexer.createIndexingContext(
104                     id, repositoryId, repository, indexDirectory, repositoryUrl, indexUpdateUrl, true, false, indexers);
105             indexingContexts.put(context.getId(), context);
106             return context;
107         } catch (ExistingLuceneIndexMismatchException e) {
108             throw new UnsupportedExistingLuceneIndexException(e.getMessage(), e);
109         }
110     }
111 
112     public IndexingContext addIndexingContextForced(
113             String id,
114             String repositoryId,
115             File repository,
116             File indexDirectory,
117             String repositoryUrl,
118             String indexUpdateUrl,
119             List<? extends IndexCreator> indexers)
120             throws IOException {
121         IndexingContext context = indexer.createIndexingContext(
122                 id, repositoryId, repository, indexDirectory, repositoryUrl, indexUpdateUrl, true, true, indexers);
123         indexingContexts.put(context.getId(), context);
124         return context;
125     }
126 
127     public IndexingContext addIndexingContext(
128             String id,
129             String repositoryId,
130             File repository,
131             Directory directory,
132             String repositoryUrl,
133             String indexUpdateUrl,
134             List<? extends IndexCreator> indexers)
135             throws IOException, UnsupportedExistingLuceneIndexException {
136         try {
137             IndexingContext context = new DefaultIndexingContext(
138                     id,
139                     repositoryId,
140                     repository,
141                     directory,
142                     repositoryUrl,
143                     indexUpdateUrl,
144                     IndexCreatorSorter.sort(indexers),
145                     false);
146             indexingContexts.put(context.getId(), context);
147             return context;
148         } catch (ExistingLuceneIndexMismatchException e) {
149             throw new UnsupportedExistingLuceneIndexException(e.getMessage(), e);
150         }
151     }
152 
153     public IndexingContext addIndexingContextForced(
154             String id,
155             String repositoryId,
156             File repository,
157             Directory directory,
158             String repositoryUrl,
159             String indexUpdateUrl,
160             List<? extends IndexCreator> indexers)
161             throws IOException {
162         IndexingContext context = new DefaultIndexingContext(
163                 id,
164                 repositoryId,
165                 repository,
166                 directory,
167                 repositoryUrl,
168                 indexUpdateUrl,
169                 IndexCreatorSorter.sort(indexers),
170                 true);
171         indexingContexts.put(context.getId(), context);
172         return context;
173     }
174 
175     public IndexingContext addMergedIndexingContext(
176             String id,
177             String repositoryId,
178             File repository,
179             File indexDirectory,
180             boolean searchable,
181             Collection<IndexingContext> contexts)
182             throws IOException {
183         return addMergedIndexingContext(
184                 id, repositoryId, repository, indexDirectory, searchable, new StaticContextMemberProvider(contexts));
185     }
186 
187     public IndexingContext addMergedIndexingContext(
188             String id,
189             String repositoryId,
190             File repository,
191             File indexDirectory,
192             boolean searchable,
193             ContextMemberProvider membersProvider)
194             throws IOException {
195         IndexingContext context = indexer.createMergedIndexingContext(
196                 id, repositoryId, repository, indexDirectory, searchable, membersProvider);
197         indexingContexts.put(context.getId(), context);
198         return context;
199     }
200 
201     public IndexingContext addMergedIndexingContext(
202             String id,
203             String repositoryId,
204             File repository,
205             Directory indexDirectory,
206             boolean searchable,
207             Collection<IndexingContext> contexts)
208             throws IOException {
209         IndexingContext context = new MergedIndexingContext(
210                 id, repositoryId, repository, indexDirectory, searchable, new StaticContextMemberProvider(contexts));
211         indexingContexts.put(context.getId(), context);
212         return context;
213     }
214 
215     public IndexingContext addMergedIndexingContext(
216             String id,
217             String repositoryId,
218             File repository,
219             Directory indexDirectory,
220             boolean searchable,
221             ContextMemberProvider membersProvider)
222             throws IOException {
223         IndexingContext context =
224                 new MergedIndexingContext(id, repositoryId, repository, indexDirectory, searchable, membersProvider);
225         indexingContexts.put(context.getId(), context);
226         return context;
227     }
228 
229     public void removeIndexingContext(IndexingContext context, boolean deleteFiles) throws IOException {
230         if (indexingContexts.containsKey(context.getId())) {
231             indexingContexts.remove(context.getId());
232             indexer.closeIndexingContext(context, deleteFiles);
233         }
234     }
235 
236     public Map<String, IndexingContext> getIndexingContexts() {
237         return Collections.unmodifiableMap(indexingContexts);
238     }
239 
240     // ----------------------------------------------------------------------------
241     // Scanning
242     // ----------------------------------------------------------------------------
243 
244     public void scan(final IndexingContext context) throws IOException {
245         scan(context, null);
246     }
247 
248     public void scan(final IndexingContext context, boolean update) throws IOException {
249         scan(context, null, update);
250     }
251 
252     public void scan(final IndexingContext context, final ArtifactScanningListener listener) throws IOException {
253         scan(context, listener, false);
254     }
255 
256     public void scan(final IndexingContext context, final ArtifactScanningListener listener, final boolean update)
257             throws IOException {
258         scan(context, null, listener, update);
259     }
260 
261     /**
262      * Uses {@link Scanner} to scan repository content. A {@link ArtifactScanningListener} is used to process found
263      * artifacts and to add them to the index using
264      * {@link NexusIndexer#artifactDiscovered(ArtifactContext, IndexingContext)}.
265      *
266      * @see DefaultScannerListener
267      * @see #artifactDiscovered(ArtifactContext, IndexingContext)
268      */
269     public void scan(
270             final IndexingContext context,
271             final String fromPath,
272             final ArtifactScanningListener listener,
273             final boolean update)
274             throws IOException {
275         final File repositoryDirectory = context.getRepository();
276         if (repositoryDirectory == null) {
277             // nothing to scan
278             return;
279         }
280 
281         if (!repositoryDirectory.exists()) {
282             throw new IOException("Repository directory " + repositoryDirectory + " does not exist");
283         }
284 
285         // always use temporary context when reindexing
286         final File tmpDir = Files.createTempDirectory(context.getId() + "-tmp").toFile();
287         IndexingContext tmpContext = null;
288         try {
289             final FSDirectory directory = FSDirectory.open(tmpDir.toPath());
290             if (update) {
291                 IndexUtils.copyDirectory(context.getIndexDirectory(), directory);
292             }
293             tmpContext = new DefaultIndexingContext(
294                     context.getId() + "-tmp", //
295                     context.getRepositoryId(), //
296                     context.getRepository(), //
297                     directory, //
298                     context.getRepositoryUrl(), //
299                     context.getIndexUpdateUrl(), //
300                     context.getIndexCreators(), //
301                     true);
302 
303             scanner.scan(new ScanningRequest(
304                     tmpContext, //
305                     new DefaultScannerListener(
306                             tmpContext, indexerEngine,
307                             update, listener),
308                     fromPath));
309 
310             tmpContext.updateTimestamp(true);
311             context.replace(tmpContext.getIndexDirectory());
312         } catch (Exception ex) {
313             throw new IOException("Error scanning context " + context.getId() + ": " + ex, ex);
314         } finally {
315             if (tmpContext != null) {
316                 tmpContext.close(true);
317             }
318 
319             FileUtils.deleteDirectory(tmpDir);
320         }
321     }
322 
323     /**
324      * Delegates to the {@link IndexerEngine} to add a new artifact to the index
325      */
326     public void artifactDiscovered(ArtifactContext ac, IndexingContext context) throws IOException {
327         if (ac != null) {
328             indexerEngine.index(context, ac);
329         }
330     }
331 
332     // ----------------------------------------------------------------------------
333     // Modifying
334     // ----------------------------------------------------------------------------
335 
336     /**
337      * Delegates to the {@link IndexerEngine} to update artifact to the index
338      */
339     public void addArtifactToIndex(ArtifactContext ac, IndexingContext context) throws IOException {
340         indexer.addArtifactsToIndex(Collections.singleton(ac), context);
341     }
342 
343     public void addArtifactsToIndex(Collection<ArtifactContext> acs, IndexingContext context) throws IOException {
344         indexer.addArtifactsToIndex(acs, context);
345     }
346 
347     /**
348      * Delegates to the {@link IndexerEngine} to remove artifact from the index
349      */
350     public void deleteArtifactFromIndex(ArtifactContext ac, IndexingContext context) throws IOException {
351         indexer.deleteArtifactsFromIndex(Collections.singleton(ac), context);
352     }
353 
354     public void deleteArtifactsFromIndex(Collection<ArtifactContext> acs, IndexingContext context) throws IOException {
355         indexer.deleteArtifactsFromIndex(acs, context);
356     }
357 
358     // ----------------------------------------------------------------------------
359     // Searching
360     // ----------------------------------------------------------------------------
361 
362     public FlatSearchResponse searchFlat(FlatSearchRequest request) throws IOException {
363         if (request.getContexts().isEmpty()) {
364             request.getContexts().addAll(getIndexingContexts().values());
365         }
366         return indexer.searchFlat(request);
367     }
368 
369     public IteratorSearchResponse searchIterator(IteratorSearchRequest request) throws IOException {
370         if (request.getContexts().isEmpty()) {
371             request.getContexts().addAll(getIndexingContexts().values());
372         }
373         return indexer.searchIterator(request);
374     }
375 
376     public GroupedSearchResponse searchGrouped(GroupedSearchRequest request) throws IOException {
377         if (request.getContexts().isEmpty()) {
378             request.getContexts().addAll(getIndexingContexts().values());
379         }
380         return indexer.searchGrouped(request);
381     }
382 
383     // ----------------------------------------------------------------------------
384     // Query construction
385     // ----------------------------------------------------------------------------
386 
387     @Deprecated
388     public Query constructQuery(Field field, String query, SearchType type) throws IllegalArgumentException {
389         try {
390             return queryCreator.constructQuery(field, query, type);
391         } catch (ParseException e) {
392             throw new IllegalArgumentException(e);
393         }
394     }
395 
396     public Query constructQuery(Field field, SearchExpression expression) throws IllegalArgumentException {
397         return indexer.constructQuery(field, expression);
398     }
399 
400     // ----------------------------------------------------------------------------
401     // Identification
402     // ----------------------------------------------------------------------------
403 
404     public Collection<ArtifactInfo> identify(Field field, String query) throws IllegalArgumentException, IOException {
405         return identify(constructQuery(field, query, SearchType.EXACT));
406     }
407 
408     public Collection<ArtifactInfo> identify(File artifact) throws IOException {
409         return identify(artifact, indexingContexts.values());
410     }
411 
412     public Collection<ArtifactInfo> identify(File artifact, Collection<IndexingContext> contexts) throws IOException {
413         return indexer.identify(artifact, contexts);
414     }
415 
416     public Collection<ArtifactInfo> identify(Query query) throws IOException {
417         return identify(query, indexingContexts.values());
418     }
419 
420     public Collection<ArtifactInfo> identify(Query query, Collection<IndexingContext> contexts) throws IOException {
421         return indexer.identify(query, contexts);
422     }
423 }