001package org.apache.archiva.consumers.core; 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.beans.ManagedRepository; 023import org.apache.archiva.configuration.ArchivaConfiguration; 024import org.apache.archiva.configuration.FileTypes; 025import org.apache.archiva.consumers.AbstractMonitoredConsumer; 026import org.apache.archiva.consumers.ConsumerException; 027import org.apache.archiva.consumers.KnownRepositoryContentConsumer; 028import org.apache.archiva.model.ArtifactReference; 029import org.apache.archiva.model.ProjectReference; 030import org.apache.archiva.model.VersionedReference; 031import org.apache.archiva.repository.ContentNotFoundException; 032import org.apache.archiva.repository.ManagedRepositoryContent; 033import org.apache.archiva.repository.RepositoryContentFactory; 034import org.apache.archiva.repository.RepositoryException; 035import org.apache.archiva.repository.RepositoryNotFoundException; 036import org.apache.archiva.repository.layout.LayoutException; 037import org.apache.archiva.repository.metadata.MetadataTools; 038import org.apache.archiva.repository.metadata.RepositoryMetadataException; 039import org.slf4j.Logger; 040import org.slf4j.LoggerFactory; 041import org.springframework.context.annotation.Scope; 042import org.springframework.stereotype.Service; 043 044import javax.annotation.PostConstruct; 045import javax.inject.Inject; 046import java.io.File; 047import java.io.IOException; 048import java.util.ArrayList; 049import java.util.Date; 050import java.util.List; 051 052/** 053 * MetadataUpdaterConsumer will create and update the metadata present within the repository. 054 */ 055@Service( "knownRepositoryContentConsumer#metadata-updater" ) 056@Scope( "prototype" ) 057public class MetadataUpdaterConsumer 058 extends AbstractMonitoredConsumer 059 implements KnownRepositoryContentConsumer 060 // it's prototype bean so we assume configuration won't change during a run 061 //, RegistryListener 062{ 063 private Logger log = LoggerFactory.getLogger( MetadataUpdaterConsumer.class ); 064 065 /** 066 * default-value="metadata-updater" 067 */ 068 private String id = "metadata-updater"; 069 070 private String description = "Update / Create maven-metadata.xml files"; 071 072 @Inject 073 private RepositoryContentFactory repositoryFactory; 074 075 @Inject 076 private MetadataTools metadataTools; 077 078 @Inject 079 private ArchivaConfiguration configuration; 080 081 @Inject 082 private FileTypes filetypes; 083 084 private static final String TYPE_METADATA_BAD_INTERNAL_REF = "metadata-bad-internal-ref"; 085 086 private static final String TYPE_METADATA_WRITE_FAILURE = "metadata-write-failure"; 087 088 private static final String TYPE_METADATA_IO = "metadata-io-warning"; 089 090 private ManagedRepositoryContent repository; 091 092 private File repositoryDir; 093 094 private List<String> includes = new ArrayList<>( 0 ); 095 096 private long scanStartTimestamp = 0; 097 098 @Override 099 public String getDescription( ) 100 { 101 return description; 102 } 103 104 @Override 105 public String getId( ) 106 { 107 return id; 108 } 109 110 public void setIncludes( List<String> includes ) 111 { 112 this.includes = includes; 113 } 114 115 @Override 116 public void beginScan( ManagedRepository repoConfig, Date whenGathered ) 117 throws ConsumerException 118 { 119 try 120 { 121 this.repository = repositoryFactory.getManagedRepositoryContent( repoConfig.getId( ) ); 122 this.repositoryDir = new File( repository.getRepoRoot( ) ); 123 this.scanStartTimestamp = System.currentTimeMillis( ); 124 } 125 catch ( RepositoryNotFoundException e ) 126 { 127 throw new ConsumerException( e.getMessage( ), e ); 128 } 129 catch ( RepositoryException e ) 130 { 131 throw new ConsumerException( e.getMessage( ), e ); 132 } 133 } 134 135 @Override 136 public void beginScan( ManagedRepository repository, Date whenGathered, boolean executeOnEntireRepo ) 137 throws ConsumerException 138 { 139 beginScan( repository, whenGathered ); 140 } 141 142 @Override 143 public void completeScan( ) 144 { 145 /* do nothing here */ 146 } 147 148 @Override 149 public void completeScan( boolean executeOnEntireRepo ) 150 { 151 completeScan( ); 152 } 153 154 @Override 155 public List<String> getExcludes( ) 156 { 157 return getDefaultArtifactExclusions( ); 158 } 159 160 @Override 161 public List<String> getIncludes( ) 162 { 163 return this.includes; 164 } 165 166 @Override 167 public void processFile( String path ) 168 throws ConsumerException 169 { 170 // Ignore paths like .index etc 171 if ( !path.startsWith( "." ) ) 172 { 173 try 174 { 175 ArtifactReference artifact = repository.toArtifactReference( path ); 176 updateVersionMetadata( artifact, path ); 177 updateProjectMetadata( artifact, path ); 178 } 179 catch ( LayoutException e ) 180 { 181 log.info( "Not processing path that is not an artifact: {} ({})", path, e.getMessage( ) ); 182 } 183 } 184 } 185 186 @Override 187 public void processFile( String path, boolean executeOnEntireRepo ) 188 throws Exception 189 { 190 processFile( path ); 191 } 192 193 private void updateProjectMetadata( ArtifactReference artifact, String path ) 194 { 195 ProjectReference projectRef = new ProjectReference( ); 196 projectRef.setGroupId( artifact.getGroupId( ) ); 197 projectRef.setArtifactId( artifact.getArtifactId( ) ); 198 199 try 200 { 201 String metadataPath = this.metadataTools.toPath( projectRef ); 202 203 File projectMetadata = new File( this.repositoryDir, metadataPath ); 204 205 if ( projectMetadata.exists( ) && ( projectMetadata.lastModified( ) >= this.scanStartTimestamp ) ) 206 { 207 // This metadata is up to date. skip it. 208 log.debug( "Skipping uptodate metadata: {}", this.metadataTools.toPath( projectRef ) ); 209 return; 210 } 211 212 metadataTools.updateMetadata( this.repository, projectRef ); 213 log.debug( "Updated metadata: {}", this.metadataTools.toPath( projectRef ) ); 214 } 215 catch ( LayoutException e ) 216 { 217 log.warn( "Unable to convert path [{}] to an internal project reference: ", path, e ); 218 triggerConsumerWarning( TYPE_METADATA_BAD_INTERNAL_REF, 219 "Unable to convert path [" + path + "] to an internal project reference: " 220 + e.getMessage( ) ); 221 } 222 catch ( RepositoryMetadataException e ) 223 { 224 log.error( "Unable to write project metadat for artifact [{}]:", path, e ); 225 triggerConsumerError( TYPE_METADATA_WRITE_FAILURE, 226 "Unable to write project metadata for artifact [" + path + "]: " + e.getMessage( ) ); 227 } 228 catch ( IOException e ) 229 { 230 log.warn( "Project metadata not written due to IO warning: ", e ); 231 triggerConsumerWarning( TYPE_METADATA_IO, 232 "Project metadata not written due to IO warning: " + e.getMessage( ) ); 233 } 234 catch ( ContentNotFoundException e ) 235 { 236 log.warn( "Project metadata not written because no versions were found to update: ", e ); 237 triggerConsumerWarning( TYPE_METADATA_IO, 238 "Project metadata not written because no versions were found to update: " 239 + e.getMessage( ) ); 240 } 241 } 242 243 private void updateVersionMetadata( ArtifactReference artifact, String path ) 244 { 245 VersionedReference versionRef = new VersionedReference( ); 246 versionRef.setGroupId( artifact.getGroupId( ) ); 247 versionRef.setArtifactId( artifact.getArtifactId( ) ); 248 versionRef.setVersion( artifact.getVersion( ) ); 249 250 try 251 { 252 String metadataPath = this.metadataTools.toPath( versionRef ); 253 254 File projectMetadata = new File( this.repositoryDir, metadataPath ); 255 256 if ( projectMetadata.exists( ) && ( projectMetadata.lastModified( ) >= this.scanStartTimestamp ) ) 257 { 258 // This metadata is up to date. skip it. 259 log.debug( "Skipping uptodate metadata: {}", this.metadataTools.toPath( versionRef ) ); 260 return; 261 } 262 263 metadataTools.updateMetadata( this.repository, versionRef ); 264 log.debug( "Updated metadata: {}", this.metadataTools.toPath( versionRef ) ); 265 } 266 catch ( LayoutException e ) 267 { 268 log.warn( "Unable to convert path [{}] to an internal version reference: ", path, e ); 269 triggerConsumerWarning( TYPE_METADATA_BAD_INTERNAL_REF, 270 "Unable to convert path [" + path + "] to an internal version reference: " 271 + e.getMessage( ) ); 272 } 273 catch ( RepositoryMetadataException e ) 274 { 275 log.error( "Unable to write version metadata for artifact [{}]: ", path, e ); 276 triggerConsumerError( TYPE_METADATA_WRITE_FAILURE, 277 "Unable to write version metadata for artifact [" + path + "]: " + e.getMessage( ) ); 278 } 279 catch ( IOException e ) 280 { 281 log.warn( "Version metadata not written due to IO warning: ", e ); 282 triggerConsumerWarning( TYPE_METADATA_IO, 283 "Version metadata not written due to IO warning: " + e.getMessage( ) ); 284 } 285 catch ( ContentNotFoundException e ) 286 { 287 log.warn( "Version metadata not written because no versions were found to update: ", e ); 288 triggerConsumerWarning( TYPE_METADATA_IO, 289 "Version metadata not written because no versions were found to update: " 290 + e.getMessage( ) ); 291 } 292 } 293 294 /* 295 @Override 296 public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue ) 297 { 298 if ( ConfigurationNames.isRepositoryScanning( propertyName ) ) 299 { 300 initIncludes(); 301 } 302 } 303 304 @Override 305 public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue ) 306 { 307 // do nothing here 308 } 309 */ 310 311 private void initIncludes( ) 312 { 313 includes = new ArrayList<>( filetypes.getFileTypePatterns( FileTypes.ARTIFACTS ) ); 314 } 315 316 @PostConstruct 317 public void initialize( ) 318 { 319 //configuration.addChangeListener( this ); 320 321 initIncludes( ); 322 } 323}