| 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 javax.inject.Inject; |
| import javax.inject.Named; |
| import javax.inject.Singleton; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.security.MessageDigest; |
| import java.security.NoSuchAlgorithmException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.List; |
| import org.apache.lucene.queryparser.classic.ParseException; |
| import org.apache.lucene.search.Query; |
| import org.apache.maven.index.context.ContextMemberProvider; |
| import org.apache.maven.index.context.DefaultIndexingContext; |
| import org.apache.maven.index.context.ExistingLuceneIndexMismatchException; |
| import org.apache.maven.index.context.IndexCreator; |
| import org.apache.maven.index.context.IndexingContext; |
| import org.apache.maven.index.context.MergedIndexingContext; |
| import org.apache.maven.index.expr.SearchExpression; |
| import org.apache.maven.index.expr.SearchTypedStringSearchExpression; |
| import org.apache.maven.index.expr.SourcedSearchExpression; |
| import org.apache.maven.index.util.IndexCreatorSorter; |
| import org.codehaus.plexus.util.IOUtil; |
| |
| /** |
| * A default {@link Indexer} implementation. |
| * |
| * @author Tamas Cservenak |
| */ |
| @Singleton |
| @Named |
| public class DefaultIndexer |
| implements Indexer |
| { |
| |
| private final SearchEngine searcher; |
| |
| private final IndexerEngine indexerEngine; |
| |
| private final QueryCreator queryCreator; |
| |
| |
| @Inject |
| public DefaultIndexer( SearchEngine searcher, |
| IndexerEngine indexerEngine, |
| QueryCreator queryCreator ) |
| { |
| this.searcher = searcher; |
| this.indexerEngine = indexerEngine; |
| this.queryCreator = queryCreator; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // Contexts |
| // ---------------------------------------------------------------------------- |
| |
| public IndexingContext createIndexingContext( String id, String repositoryId, File repository, File indexDirectory, |
| String repositoryUrl, String indexUpdateUrl, boolean searchable, |
| boolean reclaim, List<? extends IndexCreator> indexers ) |
| throws IOException, ExistingLuceneIndexMismatchException, IllegalArgumentException |
| { |
| final IndexingContext context = |
| new DefaultIndexingContext( id, repositoryId, repository, indexDirectory, repositoryUrl, indexUpdateUrl, |
| IndexCreatorSorter.sort( indexers ), reclaim ); |
| context.setSearchable( searchable ); |
| return context; |
| } |
| |
| public IndexingContext createMergedIndexingContext( String id, String repositoryId, File repository, |
| File indexDirectory, boolean searchable, |
| ContextMemberProvider membersProvider ) |
| throws IOException |
| { |
| IndexingContext context = |
| new MergedIndexingContext( id, repositoryId, repository, indexDirectory, searchable, membersProvider ); |
| return context; |
| } |
| |
| public void closeIndexingContext( IndexingContext context, boolean deleteFiles ) |
| throws IOException |
| { |
| context.close( deleteFiles ); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // Modifying |
| // ---------------------------------------------------------------------------- |
| |
| public void addArtifactToIndex( ArtifactContext ac, IndexingContext context) |
| throws IOException |
| { |
| if ( ac != null ) |
| { |
| indexerEngine.update( context, ac ); |
| |
| context.commit(); |
| } |
| } |
| |
| public void addArtifactsToIndex( Collection<ArtifactContext> ac, IndexingContext context ) |
| throws IOException |
| { |
| if ( ac != null && !ac.isEmpty() ) |
| { |
| for ( ArtifactContext actx : ac ) |
| { |
| indexerEngine.update( context, actx ); |
| } |
| |
| context.commit(); |
| } |
| } |
| |
| public void deleteArtifactsFromIndex( Collection<ArtifactContext> ac, IndexingContext context ) |
| throws IOException |
| { |
| if ( ac != null && !ac.isEmpty() ) |
| { |
| for ( ArtifactContext actx : ac ) |
| { |
| indexerEngine.remove( context, actx ); |
| |
| context.commit(); |
| } |
| } |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // Searching |
| // ---------------------------------------------------------------------------- |
| |
| public FlatSearchResponse searchFlat( FlatSearchRequest request ) |
| throws IOException |
| { |
| if ( request.getContexts().isEmpty() ) |
| { |
| return new FlatSearchResponse( request.getQuery(), 0, Collections.<ArtifactInfo> emptySet() ); |
| } |
| else |
| { |
| return searcher.forceSearchFlatPaged( request, request.getContexts() ); |
| } |
| } |
| |
| public IteratorSearchResponse searchIterator( IteratorSearchRequest request ) |
| throws IOException |
| { |
| if ( request.getContexts().isEmpty() ) |
| { |
| return IteratorSearchResponse.empty( request.getQuery() ); |
| } |
| else |
| { |
| return searcher.forceSearchIteratorPaged( request, request.getContexts() ); |
| } |
| } |
| |
| public GroupedSearchResponse searchGrouped( GroupedSearchRequest request ) |
| throws IOException |
| { |
| if ( request.getContexts().isEmpty() ) |
| { |
| return new GroupedSearchResponse( request.getQuery(), 0, Collections.<String, ArtifactInfoGroup> emptyMap() ); |
| } |
| else |
| { |
| // search targeted |
| return searcher.forceSearchGrouped( request, request.getContexts() ); |
| } |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // Identification |
| // ---------------------------------------------------------------------------- |
| |
| public Collection<ArtifactInfo> identify( final File artifact, final Collection<IndexingContext> contexts ) |
| throws IOException |
| { |
| try (FileInputStream is = new FileInputStream( artifact )) |
| { |
| final MessageDigest sha1 = MessageDigest.getInstance( "SHA-1" ); |
| final byte[] buff = new byte[4096]; |
| int n; |
| while ( ( n = is.read( buff ) ) > -1 ) |
| { |
| sha1.update( buff, 0, n ); |
| } |
| byte[] digest = sha1.digest(); |
| return identify( constructQuery( MAVEN.SHA1, new SourcedSearchExpression( encode( digest ) ) ), contexts ); |
| } |
| catch ( NoSuchAlgorithmException ex ) |
| { |
| IOException ioe = new IOException( "Unable to calculate digest" ); |
| ioe.initCause( ex ); |
| throw ioe; |
| } |
| } |
| |
| public Collection<ArtifactInfo> identify( Query query, Collection<IndexingContext> contexts ) |
| throws IOException |
| { |
| final IteratorSearchResponse result = |
| searcher.searchIteratorPaged( new IteratorSearchRequest( query ), contexts ); |
| try |
| { |
| final ArrayList<ArtifactInfo> ais = new ArrayList<ArtifactInfo>( result.getTotalHitsCount() ); |
| for ( ArtifactInfo ai : result ) |
| { |
| ais.add( ai ); |
| } |
| return ais; |
| } |
| finally |
| { |
| result.close(); |
| } |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // Query construction |
| // ---------------------------------------------------------------------------- |
| |
| public Query constructQuery( Field field, SearchExpression expression ) |
| throws IllegalArgumentException |
| { |
| try |
| { |
| return queryCreator.constructQuery( field, expression ); |
| } |
| catch ( ParseException e ) |
| { |
| throw new IllegalArgumentException( e ); |
| } |
| } |
| |
| public Query constructQuery( Field field, String expression, SearchType searchType ) |
| throws IllegalArgumentException |
| { |
| return constructQuery( field, new SearchTypedStringSearchExpression( expression, searchType )); |
| } |
| // == |
| |
| private static final char[] DIGITS = "0123456789abcdef".toCharArray(); |
| |
| private static String encode( byte[] digest ) |
| { |
| char[] buff = new char[digest.length * 2]; |
| |
| int n = 0; |
| |
| for ( byte b : digest ) |
| { |
| buff[n++] = DIGITS[( 0xF0 & b ) >> 4]; |
| buff[n++] = DIGITS[0x0F & b]; |
| } |
| |
| return new String( buff ); |
| } |
| } |