blob: 2d79561b952aa7896b810c0c854719581045ca8e [file] [log] [blame]
/*
* 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);
}
}