001package 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 021import org.apache.archiva.common.plexusbridge.MavenIndexerUtils; 022import org.apache.archiva.common.plexusbridge.PlexusSisuBridge; 023import org.apache.archiva.common.plexusbridge.PlexusSisuBridgeException; 024import org.apache.commons.io.FileUtils; 025import org.apache.commons.lang.time.StopWatch; 026import org.apache.maven.index.NexusIndexer; 027import org.apache.maven.index.context.IndexingContext; 028import org.apache.maven.index.context.UnsupportedExistingLuceneIndexException; 029import org.apache.maven.index.packer.IndexPacker; 030import org.apache.maven.index.packer.IndexPackingRequest; 031import org.slf4j.Logger; 032import org.slf4j.LoggerFactory; 033import org.springframework.scheduling.annotation.Async; 034import org.springframework.stereotype.Service; 035 036import javax.inject.Inject; 037import java.io.File; 038import java.io.IOException; 039import java.util.Collection; 040import java.util.List; 041import java.util.concurrent.CopyOnWriteArrayList; 042 043/** 044 * @author Olivier Lamy 045 * @since 1.4-M2 046 */ 047@Service("indexMerger#default") 048public class DefaultIndexMerger 049 implements IndexMerger 050{ 051 052 private Logger log = LoggerFactory.getLogger( getClass() ); 053 054 private MavenIndexerUtils mavenIndexerUtils; 055 056 private NexusIndexer indexer; 057 058 private IndexPacker indexPacker; 059 060 private List<TemporaryGroupIndex> temporaryGroupIndexes = new CopyOnWriteArrayList<>(); 061 062 private List<String> runningGroups = new CopyOnWriteArrayList<String>(); 063 064 @Inject 065 public DefaultIndexMerger( PlexusSisuBridge plexusSisuBridge, MavenIndexerUtils mavenIndexerUtils ) 066 throws PlexusSisuBridgeException 067 { 068 this.indexer = plexusSisuBridge.lookup( NexusIndexer.class ); 069 this.mavenIndexerUtils = mavenIndexerUtils; 070 indexPacker = plexusSisuBridge.lookup( IndexPacker.class, "default" ); 071 } 072 073 @Override 074 public IndexingContext buildMergedIndex( IndexMergerRequest indexMergerRequest ) 075 throws IndexMergerException 076 { 077 String groupId = indexMergerRequest.getGroupId(); 078 079 if ( runningGroups.contains( groupId ) ) 080 { 081 log.info( "skip build merge remote indexes for id: '{}' as already running", groupId ); 082 return null; 083 } 084 085 runningGroups.add( groupId ); 086 087 StopWatch stopWatch = new StopWatch(); 088 stopWatch.reset(); 089 stopWatch.start(); 090 091 File mergedIndexDirectory = indexMergerRequest.getMergedIndexDirectory(); 092 093 String tempRepoId = mergedIndexDirectory.getName(); 094 095 try 096 { 097 File indexLocation = new File( mergedIndexDirectory, indexMergerRequest.getMergedIndexPath() ); 098 IndexingContext indexingContext = 099 indexer.addIndexingContext( tempRepoId, tempRepoId, mergedIndexDirectory, indexLocation, null, null, 100 mavenIndexerUtils.getAllIndexCreators() ); 101 102 for ( String repoId : indexMergerRequest.getRepositoriesIds() ) 103 { 104 IndexingContext idxToMerge = indexer.getIndexingContexts().get( repoId ); 105 if ( idxToMerge != null ) 106 { 107 indexingContext.merge( idxToMerge.getIndexDirectory() ); 108 } 109 } 110 111 indexingContext.optimize(); 112 113 if ( indexMergerRequest.isPackIndex() ) 114 { 115 IndexPackingRequest request = new IndexPackingRequest( indexingContext, indexLocation ); 116 indexPacker.packIndex( request ); 117 } 118 119 if ( indexMergerRequest.isTemporary() ) 120 { 121 temporaryGroupIndexes.add( new TemporaryGroupIndex( mergedIndexDirectory, tempRepoId, groupId, 122 indexMergerRequest.getMergedIndexTtl() ) ); 123 } 124 stopWatch.stop(); 125 log.info( "merged index for repos {} in {} s", indexMergerRequest.getRepositoriesIds(), 126 stopWatch.getTime() ); 127 return indexingContext; 128 } 129 catch ( IOException e ) 130 { 131 throw new IndexMergerException( e.getMessage(), e ); 132 } 133 catch ( UnsupportedExistingLuceneIndexException e ) 134 { 135 throw new IndexMergerException( e.getMessage(), e ); 136 } 137 finally 138 { 139 runningGroups.remove( groupId ); 140 } 141 } 142 143 @Async 144 @Override 145 public void cleanTemporaryGroupIndex( TemporaryGroupIndex temporaryGroupIndex ) 146 { 147 if ( temporaryGroupIndex == null ) 148 { 149 return; 150 } 151 152 try 153 { 154 IndexingContext indexingContext = indexer.getIndexingContexts().get( temporaryGroupIndex.getIndexId() ); 155 if ( indexingContext != null ) 156 { 157 indexer.removeIndexingContext( indexingContext, true ); 158 } 159 File directory = temporaryGroupIndex.getDirectory(); 160 if ( directory != null && directory.exists() ) 161 { 162 FileUtils.deleteDirectory( directory ); 163 } 164 temporaryGroupIndexes.remove( temporaryGroupIndex ); 165 } 166 catch ( IOException e ) 167 { 168 log.warn( "fail to delete temporary group index {}", temporaryGroupIndex.getIndexId(), e ); 169 } 170 } 171 172 @Override 173 public Collection<TemporaryGroupIndex> getTemporaryGroupIndexes() 174 { 175 return this.temporaryGroupIndexes; 176 } 177}