001package org.apache.archiva.repository.scanner; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import org.apache.archiva.admin.model.RepositoryAdminException; 023import org.apache.archiva.admin.model.admin.ArchivaAdministration; 024import org.apache.archiva.common.utils.BaseFile; 025import org.apache.archiva.common.utils.PathUtil; 026import org.apache.archiva.configuration.ArchivaConfiguration; 027import org.apache.archiva.consumers.InvalidRepositoryContentConsumer; 028import org.apache.archiva.consumers.KnownRepositoryContentConsumer; 029import org.apache.archiva.consumers.RepositoryContentConsumer; 030import org.apache.archiva.consumers.functors.ConsumerWantsFilePredicate; 031import org.apache.archiva.components.registry.RegistryListener; 032import org.apache.archiva.repository.ManagedRepository; 033import org.apache.archiva.repository.scanner.functors.ConsumerProcessFileClosure; 034import org.apache.archiva.repository.scanner.functors.TriggerBeginScanClosure; 035import org.apache.archiva.repository.scanner.functors.TriggerScanCompletedClosure; 036import org.apache.commons.collections4.Closure; 037import org.apache.commons.collections4.CollectionUtils; 038import org.apache.commons.collections4.IterableUtils; 039import org.apache.commons.collections4.functors.IfClosure; 040import org.springframework.beans.BeansException; 041import org.springframework.context.ApplicationContext; 042import org.springframework.context.ApplicationContextAware; 043import org.springframework.stereotype.Service; 044 045import javax.inject.Inject; 046import java.nio.file.Path; 047import java.util.ArrayList; 048import java.util.Date; 049import java.util.HashMap; 050import java.util.List; 051import java.util.Map; 052 053/** 054 * RepositoryContentConsumerUtil 055 */ 056@Service("repositoryContentConsumers") 057public class RepositoryContentConsumers 058 implements ApplicationContextAware 059{ 060 061 @Inject 062 private ApplicationContext applicationContext; 063 064 private ArchivaAdministration archivaAdministration; 065 066 private List<KnownRepositoryContentConsumer> selectedKnownConsumers; 067 068 private List<InvalidRepositoryContentConsumer> selectedInvalidConsumers; 069 070 @Inject 071 private ArchivaConfiguration archivaConfiguration; 072 073 @Inject 074 public RepositoryContentConsumers( ArchivaAdministration archivaAdministration ) 075 { 076 this.archivaAdministration = archivaAdministration; 077 } 078 079 @Override 080 public void setApplicationContext( ApplicationContext applicationContext ) 081 throws BeansException 082 { 083 this.applicationContext = applicationContext; 084 } 085 086 /** 087 * <p> 088 * Get the list of Ids associated with those {@link KnownRepositoryContentConsumer} that have 089 * been selected in the configuration to execute. 090 * </p> 091 * <p> 092 * NOTE: This list can be larger and contain entries that might not exist or be available 093 * in the classpath, or as a component. 094 * </p> 095 * 096 * @return the list of consumer ids that have been selected by the configuration. 097 */ 098 public List<String> getSelectedKnownConsumerIds() 099 throws RepositoryAdminException 100 { 101 return archivaAdministration.getKnownContentConsumers(); 102 } 103 104 /** 105 * <p> 106 * Get the list of Ids associated with those {@link InvalidRepositoryContentConsumer} that have 107 * been selected in the configuration to execute. 108 * </p> 109 * <p> 110 * NOTE: This list can be larger and contain entries that might not exist or be available 111 * in the classpath, or as a component. 112 * </p> 113 * 114 * @return the list of consumer ids that have been selected by the configuration. 115 */ 116 public List<String> getSelectedInvalidConsumerIds() 117 throws RepositoryAdminException 118 { 119 return archivaAdministration.getInvalidContentConsumers(); 120 } 121 122 /** 123 * Get the map of {@link String} ids to {@link KnownRepositoryContentConsumer} implementations, 124 * for those consumers that have been selected according to the active configuration. 125 * 126 * @return the map of String ids to {@link KnownRepositoryContentConsumer} objects. 127 */ 128 public Map<String, KnownRepositoryContentConsumer> getSelectedKnownConsumersMap() 129 throws RepositoryAdminException 130 { 131 Map<String, KnownRepositoryContentConsumer> consumerMap = new HashMap<>(); 132 133 for ( KnownRepositoryContentConsumer consumer : getSelectedKnownConsumers() ) 134 { 135 consumerMap.put( consumer.getId(), consumer ); 136 } 137 138 return consumerMap; 139 } 140 141 /** 142 * Get the map of {@link String} ids to {@link InvalidRepositoryContentConsumer} implementations, 143 * for those consumers that have been selected according to the active configuration. 144 * 145 * @return the map of String ids to {@link InvalidRepositoryContentConsumer} objects. 146 */ 147 public Map<String, InvalidRepositoryContentConsumer> getSelectedInvalidConsumersMap() 148 throws RepositoryAdminException 149 { 150 Map<String, InvalidRepositoryContentConsumer> consumerMap = new HashMap<>(); 151 152 for ( InvalidRepositoryContentConsumer consumer : getSelectedInvalidConsumers() ) 153 { 154 consumerMap.put( consumer.getId(), consumer ); 155 } 156 157 return consumerMap; 158 } 159 160 /** 161 * Get the list of {@link KnownRepositoryContentConsumer} objects that are 162 * selected according to the active configuration. 163 * 164 * @return the list of {@link KnownRepositoryContentConsumer} that have been selected 165 * by the active configuration. 166 */ 167 public List<KnownRepositoryContentConsumer> getSelectedKnownConsumers() 168 throws RepositoryAdminException 169 { 170 // FIXME only for testing 171 if ( selectedKnownConsumers != null ) 172 { 173 return selectedKnownConsumers; 174 } 175 List<KnownRepositoryContentConsumer> ret = new ArrayList<>(); 176 177 List<String> knownSelected = getSelectedKnownConsumerIds(); 178 179 for ( KnownRepositoryContentConsumer consumer : getAvailableKnownConsumers() ) 180 { 181 if ( knownSelected.contains( consumer.getId() ) ) 182 { 183 ret.add( consumer ); 184 } 185 } 186 return ret; 187 } 188 189 public void releaseSelectedKnownConsumers( List<KnownRepositoryContentConsumer> repositoryContentConsumers ) 190 { 191 if ( repositoryContentConsumers == null ) 192 { 193 return; 194 } 195 for ( KnownRepositoryContentConsumer knownRepositoryContentConsumer : repositoryContentConsumers ) 196 { 197 if ( RegistryListener.class.isAssignableFrom( knownRepositoryContentConsumer.getClass() ) ) 198 { 199 archivaConfiguration.removeChangeListener( 200 RegistryListener.class.cast( knownRepositoryContentConsumer ) ); 201 } 202 } 203 } 204 205 /** 206 * Get the list of {@link InvalidRepositoryContentConsumer} objects that are 207 * selected according to the active configuration. 208 * 209 * @return the list of {@link InvalidRepositoryContentConsumer} that have been selected 210 * by the active configuration. 211 */ 212 public synchronized List<InvalidRepositoryContentConsumer> getSelectedInvalidConsumers() 213 throws RepositoryAdminException 214 { 215 216 // FIXME only for testing 217 if ( selectedInvalidConsumers != null ) 218 { 219 return selectedInvalidConsumers; 220 } 221 222 List<InvalidRepositoryContentConsumer> ret = new ArrayList<>(); 223 224 List<String> invalidSelected = getSelectedInvalidConsumerIds(); 225 226 for ( InvalidRepositoryContentConsumer consumer : getAvailableInvalidConsumers() ) 227 { 228 if ( invalidSelected.contains( consumer.getId() ) ) 229 { 230 ret.add( consumer ); 231 } 232 } 233 return ret; 234 } 235 236 237 /** 238 * Get the list of {@link KnownRepositoryContentConsumer} objects that are 239 * available and present in the classpath and as components in the IoC. 240 * 241 * @return the list of all available {@link KnownRepositoryContentConsumer} present in the classpath 242 * and as a component in the IoC. 243 */ 244 public List<KnownRepositoryContentConsumer> getAvailableKnownConsumers() 245 { 246 return new ArrayList<>( applicationContext.getBeansOfType( KnownRepositoryContentConsumer.class ).values() ); 247 } 248 249 /** 250 * Get the list of {@link InvalidRepositoryContentConsumer} objects that are 251 * available and present in the classpath and as components in the IoC. 252 * 253 * @return the list of all available {@link InvalidRepositoryContentConsumer} present in the classpath 254 * and as a component in the IoC. 255 */ 256 public List<InvalidRepositoryContentConsumer> getAvailableInvalidConsumers() 257 { 258 return new ArrayList<>( applicationContext.getBeansOfType( InvalidRepositoryContentConsumer.class ).values() ); 259 } 260 261 /** 262 * A convienence method to execute all of the active selected consumers for a 263 * particular arbitrary file. 264 * NOTE: Make sure that there is no repository scanning task executing before invoking this so as to prevent 265 * the index writer/reader of the current index-content consumer executing from getting closed. For an example, 266 * see ArchivaDavResource#executeConsumers( File ). 267 * 268 * @param repository the repository configuration to use. 269 * @param localFile the local file to execute the consumers against. 270 * @param updateRelatedArtifacts TODO 271 */ 272 public void executeConsumers( ManagedRepository repository, Path localFile, boolean updateRelatedArtifacts ) 273 throws RepositoryAdminException 274 { 275 List<KnownRepositoryContentConsumer> selectedKnownConsumers = null; 276 // Run the repository consumers 277 try 278 { 279 Closure<RepositoryContentConsumer> triggerBeginScan = new TriggerBeginScanClosure( repository, getStartTime(), false ); 280 281 selectedKnownConsumers = getSelectedKnownConsumers(); 282 283 // MRM-1212/MRM-1197 284 // - do not create missing/fix invalid checksums and update metadata when deploying from webdav since these are uploaded by maven 285 if ( !updateRelatedArtifacts ) 286 { 287 List<KnownRepositoryContentConsumer> clone = new ArrayList<>(); 288 clone.addAll( selectedKnownConsumers ); 289 290 for ( KnownRepositoryContentConsumer consumer : clone ) 291 { 292 if ( consumer.getId().equals( "create-missing-checksums" ) || consumer.getId().equals( 293 "metadata-updater" ) ) 294 { 295 selectedKnownConsumers.remove( consumer ); 296 } 297 } 298 } 299 300 List<InvalidRepositoryContentConsumer> selectedInvalidConsumers = getSelectedInvalidConsumers(); 301 IterableUtils.forEach( selectedKnownConsumers, triggerBeginScan ); 302 IterableUtils.forEach( selectedInvalidConsumers, triggerBeginScan ); 303 304 // yuck. In case you can't read this, it says 305 // "process the file if the consumer has it in the includes list, and not in the excludes list" 306 Path repoPath = PathUtil.getPathFromUri( repository.getLocation() ); 307 BaseFile baseFile = new BaseFile( repoPath.toString(), localFile.toFile() ); 308 ConsumerWantsFilePredicate predicate = new ConsumerWantsFilePredicate( repository ); 309 predicate.setBasefile( baseFile ); 310 predicate.setCaseSensitive( false ); 311 312 ConsumerProcessFileClosure closure = new ConsumerProcessFileClosure(); 313 closure.setBasefile( baseFile ); 314 closure.setExecuteOnEntireRepo( false ); 315 316 Closure<RepositoryContentConsumer> processIfWanted = IfClosure.ifClosure( predicate, closure ); 317 318 IterableUtils.forEach( selectedKnownConsumers, processIfWanted ); 319 320 if ( predicate.getWantedFileCount() <= 0 ) 321 { 322 // Nothing known processed this file. It is invalid! 323 IterableUtils.forEach( selectedInvalidConsumers, closure ); 324 } 325 326 TriggerScanCompletedClosure scanCompletedClosure = new TriggerScanCompletedClosure( repository, false ); 327 328 IterableUtils.forEach( selectedKnownConsumers, scanCompletedClosure ); 329 } 330 finally 331 { 332 /* TODO: This is never called by the repository scanner instance, so not calling here either - but it probably should be? 333 IterableUtils.forEach( availableKnownConsumers, triggerCompleteScan ); 334 IterableUtils.forEach( availableInvalidConsumers, triggerCompleteScan ); 335 */ 336 releaseSelectedKnownConsumers( selectedKnownConsumers ); 337 } 338 } 339 340 public void setSelectedKnownConsumers( List<KnownRepositoryContentConsumer> selectedKnownConsumers ) 341 { 342 this.selectedKnownConsumers = selectedKnownConsumers; 343 } 344 345 public void setSelectedInvalidConsumers( List<InvalidRepositoryContentConsumer> selectedInvalidConsumers ) 346 { 347 this.selectedInvalidConsumers = selectedInvalidConsumers; 348 } 349 350 protected Date getStartTime() 351 { 352 return new Date( System.currentTimeMillis() ); 353 } 354 355 public void setArchivaAdministration( ArchivaAdministration archivaAdministration ) 356 { 357 this.archivaAdministration = archivaAdministration; 358 } 359}