001package org.apache.archiva.consumers.metadata; 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.common.utils.VersionUtil; 023import org.apache.archiva.configuration.ArchivaConfiguration; 024import org.apache.archiva.configuration.ConfigurationNames; 025import org.apache.archiva.configuration.FileTypes; 026import org.apache.archiva.consumers.AbstractMonitoredConsumer; 027import org.apache.archiva.consumers.ConsumerException; 028import org.apache.archiva.consumers.KnownRepositoryContentConsumer; 029import org.apache.archiva.metadata.model.ArtifactMetadata; 030import org.apache.archiva.metadata.model.ProjectMetadata; 031import org.apache.archiva.metadata.model.ProjectVersionMetadata; 032import org.apache.archiva.metadata.repository.*; 033import org.apache.archiva.metadata.repository.storage.ReadMetadataRequest; 034import org.apache.archiva.metadata.repository.storage.RepositoryStorage; 035import org.apache.archiva.metadata.repository.storage.RepositoryStorageMetadataInvalidException; 036import org.apache.archiva.metadata.repository.storage.RepositoryStorageMetadataNotFoundException; 037import org.apache.archiva.metadata.repository.storage.RepositoryStorageRuntimeException; 038import org.apache.archiva.components.registry.Registry; 039import org.apache.archiva.components.registry.RegistryListener; 040import org.apache.archiva.repository.ManagedRepository; 041import org.slf4j.Logger; 042import org.slf4j.LoggerFactory; 043import org.springframework.context.annotation.Scope; 044import org.springframework.stereotype.Service; 045 046import javax.annotation.PostConstruct; 047import javax.inject.Inject; 048import javax.inject.Named; 049import java.time.ZoneId; 050import java.time.ZonedDateTime; 051import java.util.ArrayList; 052import java.util.Date; 053import java.util.List; 054 055/** 056 * Take an artifact off of disk and put it into the metadata repository. 057 */ 058@Service ("knownRepositoryContentConsumer#create-archiva-metadata") 059@Scope ("prototype") 060public class ArchivaMetadataCreationConsumer 061 extends AbstractMonitoredConsumer 062 implements KnownRepositoryContentConsumer, RegistryListener 063{ 064 private String id = "create-archiva-metadata"; 065 066 private String description = "Create basic metadata for Archiva to be able to reference the artifact"; 067 068 @Inject 069 private ArchivaConfiguration configuration; 070 071 @Inject 072 private FileTypes filetypes; 073 074 private ZonedDateTime whenGathered; 075 076 private List<String> includes = new ArrayList<>( 0 ); 077 078 /** 079 * FIXME: this could be multiple implementations and needs to be configured. 080 */ 081 @Inject 082 private RepositorySessionFactory repositorySessionFactory; 083 084 /** 085 * FIXME: this needs to be configurable based on storage type - and could also be instantiated per repo. Change to a 086 * factory. 087 */ 088 @Inject 089 @Named (value = "repositoryStorage#maven2") 090 private RepositoryStorage repositoryStorage; 091 092 private static final Logger log = LoggerFactory.getLogger( ArchivaMetadataCreationConsumer.class ); 093 094 private String repoId; 095 096 @Override 097 public String getId() 098 { 099 return this.id; 100 } 101 102 @Override 103 public String getDescription() 104 { 105 return this.description; 106 } 107 108 @Override 109 public List<String> getExcludes() 110 { 111 return getDefaultArtifactExclusions(); 112 } 113 114 @Override 115 public List<String> getIncludes() 116 { 117 return this.includes; 118 } 119 120 @Override 121 public void beginScan( ManagedRepository repo, Date whenGathered ) 122 throws ConsumerException 123 { 124 repoId = repo.getId(); 125 this.whenGathered = ZonedDateTime.ofInstant(whenGathered.toInstant(), ZoneId.of("GMT")); 126 } 127 128 @Override 129 public void beginScan( ManagedRepository repository, Date whenGathered, boolean executeOnEntireRepo ) 130 throws ConsumerException 131 { 132 beginScan( repository, whenGathered ); 133 } 134 135 @Override 136 public void processFile( String path ) 137 throws ConsumerException 138 { 139 140 RepositorySession repositorySession = null; 141 try 142 { 143 repositorySession = repositorySessionFactory.createSession(); 144 } 145 catch ( MetadataRepositoryException e ) 146 { 147 e.printStackTrace( ); 148 } 149 try 150 { 151 // note that we do minimal processing including checksums and POM information for performance of 152 // the initial scan. Any request for this information will be intercepted and populated on-demand 153 // or picked up by subsequent scans 154 155 ArtifactMetadata artifact = repositoryStorage.readArtifactMetadataFromPath( repoId, path ); 156 157 ProjectMetadata project = new ProjectMetadata(); 158 project.setNamespace( artifact.getNamespace() ); 159 project.setId( artifact.getProject() ); 160 161 String projectVersion = VersionUtil.getBaseVersion( artifact.getVersion() ); 162 163 MetadataRepository metadataRepository = repositorySession.getRepository(); 164 165 boolean createVersionMetadata = false; 166 167 // FIXME: maybe not too efficient since it may have already been read and stored for this artifact 168 ProjectVersionMetadata versionMetadata = null; 169 try 170 { 171 ReadMetadataRequest readMetadataRequest = 172 new ReadMetadataRequest().repositoryId( repoId ).namespace( artifact.getNamespace() ).projectId( 173 artifact.getProject() ).projectVersion( projectVersion ); 174 versionMetadata = repositoryStorage.readProjectVersionMetadata( readMetadataRequest ); 175 createVersionMetadata = true; 176 } 177 catch ( RepositoryStorageMetadataNotFoundException e ) 178 { 179 log.warn( "Missing or invalid POM for artifact:{} (repository:{}); creating empty metadata", path, 180 repoId ); 181 182 versionMetadata = new ProjectVersionMetadata(); 183 versionMetadata.setId( projectVersion ); 184 versionMetadata.setIncomplete( true ); 185 createVersionMetadata = true; 186 } 187 catch ( RepositoryStorageMetadataInvalidException e ) 188 { 189 log.warn( "Error occurred resolving POM for artifact:{} (repository:{}); message: {}", 190 new Object[]{ path, repoId, e.getMessage() } ); 191 } 192 193 // read the metadata and update it if it is newer or doesn't exist 194 artifact.setWhenGathered( whenGathered ); 195 metadataRepository.updateArtifact(repositorySession , repoId, project.getNamespace(), project.getId(), 196 projectVersion, artifact ); 197 if ( createVersionMetadata ) 198 { 199 metadataRepository.updateProjectVersion(repositorySession , repoId, project.getNamespace(), 200 project.getId(), versionMetadata ); 201 } 202 metadataRepository.updateProject(repositorySession , repoId, project ); 203 repositorySession.save(); 204 } 205 catch ( MetadataRepositoryException e ) 206 { 207 log.warn( 208 "Error occurred persisting metadata for artifact:{} (repository:{}); message: {}" , 209 path, repoId, e.getMessage(), e ); 210 try { 211 repositorySession.revert(); 212 } catch (MetadataSessionException ex) { 213 log.error("Reverting failed {}", ex.getMessage()); 214 } 215 } 216 catch ( RepositoryStorageRuntimeException e ) 217 { 218 log.warn( 219 "Error occurred persisting metadata for artifact:{} (repository:{}); message: {}", 220 path, repoId, e.getMessage(), e ); 221 try { 222 repositorySession.revert(); 223 } catch (MetadataSessionException ex) { 224 log.error("Reverting failed {}", ex.getMessage()); 225 } 226 } catch (MetadataSessionException e) { 227 throw new ConsumerException(e.getMessage(), e); 228 } finally 229 { 230 repositorySession.close(); 231 } 232 } 233 234 @Override 235 public void processFile( String path, boolean executeOnEntireRepo ) 236 throws ConsumerException 237 { 238 processFile( path ); 239 } 240 241 @Override 242 public void completeScan() 243 { 244 /* do nothing */ 245 } 246 247 @Override 248 public void completeScan( boolean executeOnEntireRepo ) 249 { 250 completeScan(); 251 } 252 253 @Override 254 public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue ) 255 { 256 if ( ConfigurationNames.isRepositoryScanning( propertyName ) ) 257 { 258 initIncludes(); 259 } 260 } 261 262 @Override 263 public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue ) 264 { 265 /* do nothing */ 266 } 267 268 private void initIncludes() 269 { 270 includes = new ArrayList<String>( filetypes.getFileTypePatterns( FileTypes.ARTIFACTS ) ); 271 } 272 273 @PostConstruct 274 public void initialize() 275 { 276 configuration.addChangeListener( this ); 277 278 initIncludes(); 279 } 280}