blob: a9ca2c75888e4b47d3fadac54aea578ec64313cd [file] [log] [blame]
package org.apache.maven.index;
/*
* 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.
*/
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.MultiFields;
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<String>();
private final Set<String> processedUinfos = new HashSet<String>();
private final Set<String> allGroups = new HashSet<String>();
private final Set<String> groups = new HashSet<String>();
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 = MultiFields.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 );
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 );
}
}