001package org.apache.archiva.indexer.merger.base; 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.indexer.ArchivaIndexManager; 022import org.apache.archiva.indexer.ArchivaIndexingContext; 023import org.apache.archiva.indexer.IndexCreationFailedException; 024import org.apache.archiva.indexer.merger.IndexMerger; 025import org.apache.archiva.indexer.merger.IndexMergerException; 026import org.apache.archiva.indexer.merger.IndexMergerRequest; 027import org.apache.archiva.indexer.merger.TemporaryGroupIndex; 028import org.apache.archiva.repository.Repository; 029import org.apache.archiva.repository.RepositoryRegistry; 030import org.apache.archiva.repository.storage.StorageAsset; 031import org.apache.archiva.repository.storage.StorageUtil; 032import org.apache.commons.lang3.time.StopWatch; 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035import org.springframework.scheduling.annotation.Async; 036import org.springframework.stereotype.Service; 037 038import javax.inject.Inject; 039import java.io.IOException; 040import java.util.Collection; 041import java.util.List; 042import java.util.Optional; 043import java.util.concurrent.CopyOnWriteArrayList; 044import java.util.stream.Collectors; 045 046/** 047 * @author Olivier Lamy 048 * @since 1.4-M2 049 */ 050@Service("indexMerger#default") 051public class DefaultIndexMerger 052 implements IndexMerger 053{ 054 055 @Inject 056 RepositoryRegistry repositoryRegistry; 057 058 private Logger log = LoggerFactory.getLogger( getClass() ); 059 060 private List<TemporaryGroupIndex> temporaryGroupIndexes = new CopyOnWriteArrayList<>(); 061 062 private List<ArchivaIndexingContext> temporaryContextes = new CopyOnWriteArrayList<>( ); 063 064 private List<String> runningGroups = new CopyOnWriteArrayList<>(); 065 066 @Inject 067 public DefaultIndexMerger( ) 068 { 069 } 070 071 @Override 072 public ArchivaIndexingContext buildMergedIndex(IndexMergerRequest indexMergerRequest ) 073 throws IndexMergerException 074 { 075 String groupId = indexMergerRequest.getGroupId(); 076 077 if ( runningGroups.contains( groupId ) ) 078 { 079 log.info( "skip build merge remote indexes for id: '{}' as already running", groupId ); 080 return null; 081 } 082 083 runningGroups.add( groupId ); 084 StopWatch stopWatch = new StopWatch(); 085 try { 086 stopWatch.reset(); 087 stopWatch.start(); 088 089 StorageAsset mergedIndexDirectory = indexMergerRequest.getMergedIndexDirectory(); 090 Repository destinationRepository = repositoryRegistry.getRepository(indexMergerRequest.getGroupId()); 091 092 ArchivaIndexManager idxManager = repositoryRegistry.getIndexManager(destinationRepository.getType()); 093 List<ArchivaIndexingContext> sourceContexts = indexMergerRequest.getRepositoriesIds().stream().map(id -> repositoryRegistry.getRepository(id).getIndexingContext()).collect(Collectors.toList()); 094 try { 095 ArchivaIndexingContext result = idxManager.mergeContexts(destinationRepository, sourceContexts, indexMergerRequest.isPackIndex()); 096 if ( indexMergerRequest.isTemporary() ) 097 { 098 String tempRepoId = destinationRepository.getId()+System.currentTimeMillis(); 099 temporaryGroupIndexes.add( new TemporaryGroupIndex( mergedIndexDirectory, tempRepoId, groupId, 100 indexMergerRequest.getMergedIndexTtl() ) ); 101 temporaryContextes.add(result); 102 } 103 return result; 104 } catch (IndexCreationFailedException e) { 105 throw new IndexMergerException("Index merging failed " + e.getMessage(), e); 106 } 107 108 } finally { 109 stopWatch.stop(); 110 log.info( "merged index for repos {} in {} s", indexMergerRequest.getRepositoriesIds(), 111 stopWatch.getTime() ); 112 runningGroups.remove(groupId); 113 } 114 } 115 116 @Async 117 @Override 118 public void cleanTemporaryGroupIndex( TemporaryGroupIndex temporaryGroupIndex ) 119 { 120 if ( temporaryGroupIndex == null ) 121 { 122 return; 123 } 124 125 try 126 { 127 Optional<ArchivaIndexingContext> ctxOpt = temporaryContextes.stream( ).filter( ctx -> ctx.getId( ).equals( temporaryGroupIndex.getIndexId( ) ) ).findFirst( ); 128 if (ctxOpt.isPresent()) { 129 ArchivaIndexingContext ctx = ctxOpt.get(); 130 ctx.close(true); 131 temporaryGroupIndexes.remove( temporaryGroupIndex ); 132 temporaryContextes.remove( ctx ); 133 StorageAsset directory = temporaryGroupIndex.getDirectory(); 134 if ( directory != null && directory.exists() ) 135 { 136 StorageUtil.deleteRecursively( directory ); 137 } 138 } 139 } 140 catch ( IOException e ) 141 { 142 log.warn( "fail to delete temporary group index {}", temporaryGroupIndex.getIndexId(), e ); 143 } 144 } 145 146 @Override 147 public Collection<TemporaryGroupIndex> getTemporaryGroupIndexes() 148 { 149 return this.temporaryGroupIndexes; 150 } 151}