001    package org.apache.archiva.indexer.merger;
002    /*
003     * Licensed to the Apache Software Foundation (ASF) under one
004     * or more contributor license agreements.  See the NOTICE file
005     * distributed with this work for additional information
006     * regarding copyright ownership.  The ASF licenses this file
007     * to you under the Apache License, Version 2.0 (the
008     * "License"); you may not use this file except in compliance
009     * with the License.  You may obtain a copy of the License at
010     *
011     *   http://www.apache.org/licenses/LICENSE-2.0
012     *
013     * Unless required by applicable law or agreed to in writing,
014     * software distributed under the License is distributed on an
015     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016     * KIND, either express or implied.  See the License for the
017     * specific language governing permissions and limitations
018     * under the License.
019     */
020    
021    import com.google.common.io.Files;
022    
023    import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin;
024    import org.apache.archiva.common.plexusbridge.MavenIndexerUtils;
025    import org.apache.archiva.common.plexusbridge.PlexusSisuBridge;
026    import org.apache.archiva.common.plexusbridge.PlexusSisuBridgeException;
027    import org.apache.commons.io.FileUtils;
028    import org.apache.commons.lang.time.StopWatch;
029    import org.apache.maven.index.NexusIndexer;
030    import org.apache.maven.index.context.IndexingContext;
031    import org.apache.maven.index.context.UnsupportedExistingLuceneIndexException;
032    import org.apache.maven.index.packer.IndexPacker;
033    import org.apache.maven.index.packer.IndexPackingRequest;
034    import org.slf4j.Logger;
035    import org.slf4j.LoggerFactory;
036    import org.springframework.scheduling.annotation.Async;
037    import org.springframework.stereotype.Service;
038    
039    import javax.inject.Inject;
040    import java.io.File;
041    import java.io.IOException;
042    import java.util.Collection;
043    import java.util.List;
044    import java.util.concurrent.CopyOnWriteArrayList;
045    
046    /**
047     * @author Olivier Lamy
048     * @since 1.4-M2
049     */
050    @Service("indexMerger#default")
051    public class DefaultIndexMerger
052        implements IndexMerger
053    {
054    
055        /**
056         * default tmp created group index ttl in minutes
057         */
058        static final int DEFAULT_GROUP_INDEX_TTL = 30;
059    
060        private Logger log = LoggerFactory.getLogger( getClass() );
061    
062        @Inject
063        private ManagedRepositoryAdmin managedRepositoryAdmin;
064    
065        private MavenIndexerUtils mavenIndexerUtils;
066    
067        private NexusIndexer indexer;
068    
069        private IndexPacker indexPacker;
070    
071        private List<TemporaryGroupIndex> temporaryGroupIndexes = new CopyOnWriteArrayList<TemporaryGroupIndex>();
072    
073        @Inject
074        public DefaultIndexMerger( PlexusSisuBridge plexusSisuBridge, MavenIndexerUtils mavenIndexerUtils )
075            throws PlexusSisuBridgeException
076        {
077            this.indexer = plexusSisuBridge.lookup( NexusIndexer.class );
078            this.mavenIndexerUtils = mavenIndexerUtils;
079            indexPacker = plexusSisuBridge.lookup( IndexPacker.class, "default" );
080        }
081    
082        public IndexingContext buildMergedIndex( IndexMergerRequest indexMergerRequest )
083            throws IndexMergerException
084        {
085            StopWatch stopWatch = new StopWatch();
086            stopWatch.reset();
087            stopWatch.start();
088            File tempRepoFile = Files.createTempDir();
089            tempRepoFile.deleteOnExit();
090    
091            String tempRepoId = tempRepoFile.getName();
092    
093            try
094            {
095                File indexLocation = new File( tempRepoFile, indexMergerRequest.getMergedIndexPath() );
096                IndexingContext indexingContext =
097                    indexer.addIndexingContext( tempRepoId, tempRepoId, tempRepoFile, indexLocation, null, null,
098                                                mavenIndexerUtils.getAllIndexCreators() );
099    
100                for ( String repoId : indexMergerRequest.getRepositoriesIds() )
101                {
102                    IndexingContext idxToMerge = indexer.getIndexingContexts().get( repoId );
103                    if ( idxToMerge != null )
104                    {
105                        indexingContext.merge( idxToMerge.getIndexDirectory() );
106                    }
107                }
108    
109                indexingContext.optimize();
110    
111                if ( indexMergerRequest.isPackIndex() )
112                {
113                    IndexPackingRequest request = new IndexPackingRequest( indexingContext, indexLocation );
114                    indexPacker.packIndex( request );
115                }
116                temporaryGroupIndexes.add(
117                    new TemporaryGroupIndex( tempRepoFile, tempRepoId, indexMergerRequest.getGroupId(), indexMergerRequest.getMergedIndexTtl() ) );
118                stopWatch.stop();
119                log.info( "merged index for repos {} in {} s", indexMergerRequest.getRepositoriesIds(),
120                          stopWatch.getTime() );
121                return indexingContext;
122            }
123            catch ( IOException e )
124            {
125                throw new IndexMergerException( e.getMessage(), e );
126            }
127            catch ( UnsupportedExistingLuceneIndexException e )
128            {
129                throw new IndexMergerException( e.getMessage(), e );
130            }
131        }
132    
133        @Async
134        public void cleanTemporaryGroupIndex( TemporaryGroupIndex temporaryGroupIndex )
135        {
136            if ( temporaryGroupIndex == null )
137            {
138                return;
139            }
140    
141            try
142            {
143                IndexingContext indexingContext = indexer.getIndexingContexts().get( temporaryGroupIndex.getIndexId() );
144                if ( indexingContext != null )
145                {
146                    indexer.removeIndexingContext( indexingContext, true );
147                }
148                File directory = temporaryGroupIndex.getDirectory();
149                if ( directory != null && directory.exists() )
150                {
151                    FileUtils.deleteDirectory( directory );
152                }
153                temporaryGroupIndexes.remove( temporaryGroupIndex );
154            }
155            catch ( IOException e )
156            {
157                log.warn( "fail to delete temporary group index {}", temporaryGroupIndex.getIndexId(), e );
158            }
159        }
160    
161        public Collection<TemporaryGroupIndex> getTemporaryGroupIndexes()
162        {
163            return this.temporaryGroupIndexes;
164        }
165    }