| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.maven.index; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.apache.lucene.document.Document; |
| import org.apache.lucene.index.CorruptIndexException; |
| import org.apache.lucene.index.IndexReader; |
| import org.apache.lucene.index.MultiBits; |
| import org.apache.lucene.index.Term; |
| import org.apache.lucene.search.IndexSearcher; |
| import org.apache.lucene.search.TermQuery; |
| import org.apache.lucene.search.TopScoreDocCollector; |
| import org.apache.lucene.util.Bits; |
| import org.apache.maven.index.context.IndexingContext; |
| |
| /** |
| * A default scanning listener |
| * |
| * @author Eugene Kuleshov |
| */ |
| public class DefaultScannerListener implements ArtifactScanningListener { |
| private final IndexingContext context; |
| |
| private final IndexerEngine indexerEngine; |
| |
| private final boolean update; |
| |
| private final ArtifactScanningListener listener; |
| |
| private final Set<String> uinfos = new HashSet<>(); |
| |
| private final Set<String> processedUinfos = new HashSet<>(); |
| |
| private final Set<String> allGroups = new HashSet<>(); |
| |
| private final Set<String> groups = new HashSet<>(); |
| |
| private final List<Exception> exceptions = new ArrayList<>(); |
| |
| private int count = 0; |
| |
| public DefaultScannerListener( |
| IndexingContext context, // |
| IndexerEngine indexerEngine, |
| boolean update, // |
| ArtifactScanningListener listener) { |
| this.context = context; |
| this.indexerEngine = indexerEngine; |
| this.update = update; |
| this.listener = listener; |
| } |
| |
| public void scanningStarted(IndexingContext ctx) { |
| try { |
| if (update) { |
| initialize(ctx); |
| } |
| } catch (IOException ex) { |
| exceptions.add(ex); |
| } |
| |
| if (listener != null) { |
| listener.scanningStarted(ctx); |
| } |
| } |
| |
| public void artifactDiscovered(ArtifactContext ac) { |
| String uinfo = ac.getArtifactInfo().getUinfo(); |
| |
| // TODO: scattered across commented out changes while I was fixing NEXUS-2712, cstamas |
| // These changes should be applied by borks too much the fragile indexer |
| |
| // if ( VersionUtils.isSnapshot( ac.getArtifactInfo().version ) && processedUinfos.contains( uinfo ) ) |
| if (processedUinfos.contains(uinfo)) { |
| return; // skip individual snapshots |
| } |
| |
| boolean adding = processedUinfos.add(uinfo); |
| |
| if (uinfos.contains(uinfo)) { |
| // already indexed |
| uinfos.remove(uinfo); |
| return; |
| } |
| |
| try { |
| if (listener != null) { |
| listener.artifactDiscovered(ac); |
| } |
| |
| if (adding) { |
| indexerEngine.index(context, ac); |
| } else { |
| indexerEngine.update(context, ac); |
| } |
| |
| for (Exception e : ac.getErrors()) { |
| artifactError(ac, e); |
| } |
| |
| groups.add(ac.getArtifactInfo().getRootGroup()); |
| allGroups.add(ac.getArtifactInfo().getGroupId()); |
| |
| count++; |
| } catch (IOException ex) { |
| artifactError(ac, ex); |
| } |
| } |
| |
| public void scanningFinished(IndexingContext ctx, ScanningResult result) { |
| result.setTotalFiles(count); |
| |
| for (Exception ex : exceptions) { |
| result.addException(ex); |
| } |
| |
| try { |
| context.optimize(); |
| |
| context.setRootGroups(groups); |
| |
| context.setAllGroups(allGroups); |
| |
| if (update && !context.isReceivingUpdates()) { |
| removeDeletedArtifacts(context, result, result.getRequest().getStartingPath()); |
| } |
| } catch (IOException ex) { |
| result.addException(ex); |
| } |
| |
| if (listener != null) { |
| listener.scanningFinished(ctx, result); |
| } |
| |
| if (result.getDeletedFiles() > 0 || result.getTotalFiles() > 0) { |
| try { |
| context.updateTimestamp(true); |
| |
| context.optimize(); |
| } catch (Exception ex) { |
| result.addException(ex); |
| } |
| } |
| } |
| |
| public void artifactError(ArtifactContext ac, Exception e) { |
| exceptions.add(e); |
| |
| if (listener != null) { |
| listener.artifactError(ac, e); |
| } |
| } |
| |
| private void initialize(IndexingContext ctx) throws IOException, CorruptIndexException { |
| final IndexSearcher indexSearcher = ctx.acquireIndexSearcher(); |
| try { |
| final IndexReader r = indexSearcher.getIndexReader(); |
| Bits liveDocs = MultiBits.getLiveDocs(r); |
| |
| for (int i = 0; i < r.maxDoc(); i++) { |
| if (liveDocs == null || liveDocs.get(i)) { |
| Document d = r.document(i); |
| |
| String uinfo = d.get(ArtifactInfo.UINFO); |
| |
| if (uinfo != null) { |
| // if ctx is receiving updates (in other words, is a proxy), |
| // there is no need to build a huge Set of strings with all uinfo's |
| // as deletion detection in those cases have no effect. Also, the |
| // removeDeletedArtifacts() method, that uses info gathered in this set |
| // is invoked with same condition. As indexes of Central are getting huge, |
| // the set grows enormously too, but is actually not used |
| if (!ctx.isReceivingUpdates()) { |
| uinfos.add(uinfo); |
| } |
| |
| // add all existing groupIds to the lists, as they will |
| // not be "discovered" and would be missing from the new list.. |
| String groupId = uinfo.substring(0, uinfo.indexOf('|')); |
| int n = groupId.indexOf('.'); |
| groups.add(n == -1 ? groupId : groupId.substring(0, n)); |
| allGroups.add(groupId); |
| } |
| } |
| } |
| } finally { |
| ctx.releaseIndexSearcher(indexSearcher); |
| } |
| } |
| |
| private void removeDeletedArtifacts(IndexingContext context, ScanningResult result, String contextPath) |
| throws IOException { |
| int deleted = 0; |
| |
| final IndexSearcher indexSearcher = context.acquireIndexSearcher(); |
| try { |
| for (String uinfo : uinfos) { |
| TopScoreDocCollector collector = TopScoreDocCollector.create(1, Integer.MAX_VALUE); |
| |
| indexSearcher.search(new TermQuery(new Term(ArtifactInfo.UINFO, uinfo)), collector); |
| |
| if (collector.getTotalHits() > 0) { |
| String[] ra = ArtifactInfo.FS_PATTERN.split(uinfo); |
| |
| ArtifactInfo ai = new ArtifactInfo(); |
| |
| ai.setRepository(context.getRepositoryId()); |
| |
| ai.setGroupId(ra[0]); |
| |
| ai.setArtifactId(ra[1]); |
| |
| ai.setVersion(ra[2]); |
| |
| if (ra.length > 3) { |
| ai.setClassifier(ArtifactInfo.renvl(ra[3])); |
| } |
| |
| if (ra.length > 4) { |
| ai.setPackaging(ArtifactInfo.renvl(ra[4])); |
| } |
| |
| // minimal ArtifactContext for removal |
| ArtifactContext ac = new ArtifactContext(null, null, null, ai, ai.calculateGav()); |
| |
| for (int i = 0; i < collector.getTotalHits(); i++) { |
| if (contextPath == null |
| || context.getGavCalculator() |
| .gavToPath(ac.getGav()) |
| .startsWith(contextPath)) { |
| indexerEngine.remove(context, ac); |
| } |
| |
| deleted++; |
| } |
| } |
| } |
| } finally { |
| context.releaseIndexSearcher(indexSearcher); |
| } |
| |
| if (deleted > 0) { |
| context.commit(); |
| } |
| |
| result.setDeletedFiles(deleted); |
| } |
| } |