package org.apache.archiva.repository.base; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import org.apache.archiva.components.registry.RegistryException; import org.apache.archiva.configuration.ArchivaConfiguration; import org.apache.archiva.configuration.Configuration; import org.apache.archiva.configuration.ConfigurationEvent; import org.apache.archiva.configuration.ConfigurationListener; import org.apache.archiva.configuration.IndeterminateConfigurationException; import org.apache.archiva.configuration.ManagedRepositoryConfiguration; import org.apache.archiva.configuration.ProxyConnectorConfiguration; import org.apache.archiva.configuration.RemoteRepositoryConfiguration; import org.apache.archiva.configuration.RepositoryGroupConfiguration; import org.apache.archiva.event.Event; import org.apache.archiva.event.EventHandler; import org.apache.archiva.event.EventManager; import org.apache.archiva.event.EventType; import org.apache.archiva.indexer.ArchivaIndexManager; import org.apache.archiva.indexer.ArchivaIndexingContext; import org.apache.archiva.indexer.IndexCreationFailedException; import org.apache.archiva.indexer.IndexManagerFactory; import org.apache.archiva.indexer.IndexUpdateFailedException; import org.apache.archiva.repository.base.group.RepositoryGroupHandler; import org.apache.archiva.repository.validation.CheckedResult; import org.apache.archiva.repository.EditableManagedRepository; import org.apache.archiva.repository.EditableRemoteRepository; import org.apache.archiva.repository.EditableRepository; import org.apache.archiva.repository.ManagedRepository; import org.apache.archiva.repository.RemoteRepository; import org.apache.archiva.repository.Repository; import org.apache.archiva.repository.RepositoryContentFactory; import org.apache.archiva.repository.RepositoryException; import org.apache.archiva.repository.RepositoryGroup; import org.apache.archiva.repository.RepositoryProvider; import org.apache.archiva.repository.RepositoryRegistry; import org.apache.archiva.repository.RepositoryType; import org.apache.archiva.repository.UnsupportedRepositoryTypeException; import org.apache.archiva.repository.event.LifecycleEvent; import org.apache.archiva.repository.event.RepositoryEvent; import org.apache.archiva.repository.event.RepositoryIndexEvent; import org.apache.archiva.repository.event.RepositoryRegistryEvent; import org.apache.archiva.repository.features.IndexCreationFeature; import org.apache.archiva.repository.features.StagingRepositoryFeature; import org.apache.archiva.repository.metadata.MetadataReader; import org.apache.archiva.repository.storage.StorageAsset; import org.apache.archiva.repository.validation.RepositoryValidator; import org.apache.archiva.repository.validation.ValidationError; import org.apache.archiva.repository.validation.ValidationResponse; import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.inject.Inject; import javax.inject.Named; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Collectors; import java.util.stream.Stream; /** * Registry for repositories. This is the central entry point for repositories. It provides methods for * retrieving, adding and removing repositories. *
* The modification methods addXX and removeXX persist the changes immediately to the configuration. If the * configuration save fails the changes are rolled back. *
* TODO: Audit events
*
* @since 3.0
*/
@Service( "repositoryRegistry" )
public class ArchivaRepositoryRegistry implements ConfigurationListener, EventHandlernull
)
* @return the new or updated repository
* @throws RepositoryException if the configuration cannot be saved or updated
*/
@Override
public ManagedRepository putRepository( ManagedRepositoryConfiguration managedRepositoryConfiguration, Configuration configuration ) throws RepositoryException
{
rwLock.writeLock( ).lock( );
try
{
final String id = managedRepositoryConfiguration.getId( );
final RepositoryType repoType = RepositoryType.valueOf( managedRepositoryConfiguration.getType( ) );
ManagedRepository repo;
boolean registeredNew = false;
repo = managedRepositories.get( id );
if ( repo != null && repo.isOpen( ) )
{
if ( repo instanceof EditableManagedRepository )
{
getProvider( repoType ).updateManagedInstance( (EditableManagedRepository) repo, managedRepositoryConfiguration );
}
else
{
throw new RepositoryException( "The repository is not editable " + id );
}
}
else
{
repo = getProvider( repoType ).createManagedInstance( managedRepositoryConfiguration );
managedRepositories.put( id, repo );
registeredNew = true;
}
updateRepositoryReferences( getProvider( repoType ), repo, managedRepositoryConfiguration, configuration );
replaceOrAddRepositoryConfig( managedRepositoryConfiguration, configuration );
if ( registeredNew )
{
pushEvent( new LifecycleEvent( LifecycleEvent.REGISTERED, this, repo ) );
}
else
{
pushEvent( new LifecycleEvent( LifecycleEvent.UPDATED, this, repo ) );
}
return repo;
}
finally
{
rwLock.writeLock( ).unlock( );
}
}
/**
* Adds a new repository group to the current list, or replaces the repository group definition with
* the same id, if it exists already.
* The change is saved to the configuration immediately.
*
* @param repositoryGroup the new repository group.
* @throws RepositoryException if the new repository group could not be saved to the configuration.
*/
@Override
public RepositoryGroup putRepositoryGroup( RepositoryGroup repositoryGroup ) throws RepositoryException
{
rwLock.writeLock( ).lock( );
try
{
if ( this.groupHandler == null )
{
throw new RepositoryException( "Fatal error. RepositoryGroupHandler not registered!" );
}
return this.groupHandler.put( repositoryGroup );
}
finally
{
rwLock.writeLock( ).unlock( );
}
}
/**
* Adds a new repository group or updates the repository with the same id, if it exists already.
* The configuration is saved immediately.
*
* @param repositoryGroupConfiguration the repository configuration
* @return the updated or created repository
* @throws RepositoryException if an error occurs, or the configuration is not valid.
*/
@Override
public RepositoryGroup putRepositoryGroup( RepositoryGroupConfiguration repositoryGroupConfiguration ) throws RepositoryException
{
rwLock.writeLock( ).lock( );
try
{
return groupHandler.put( repositoryGroupConfiguration );
}
finally
{
rwLock.writeLock( ).unlock( );
}
}
@Override
public CheckedResultnull
, the configuration is not saved.
* @return The new or updated repository group
* @throws RepositoryException if the configuration cannot be saved or updated
*/
@Override
public RepositoryGroup putRepositoryGroup( RepositoryGroupConfiguration repositoryGroupConfiguration, Configuration configuration ) throws RepositoryException
{
rwLock.writeLock( ).lock( );
try
{
return groupHandler.put( repositoryGroupConfiguration, configuration );
}
finally
{
rwLock.writeLock( ).unlock( );
}
}
private void replaceOrAddRepositoryConfig( ManagedRepositoryConfiguration managedRepositoryConfiguration, Configuration configuration )
{
if ( configuration != null )
{
ManagedRepositoryConfiguration oldCfg = configuration.findManagedRepositoryById( managedRepositoryConfiguration.getId( ) );
if ( oldCfg != null )
{
configuration.removeManagedRepository( oldCfg );
}
configuration.addManagedRepository( managedRepositoryConfiguration );
}
}
private void replaceOrAddRepositoryConfig( RemoteRepositoryConfiguration remoteRepositoryConfiguration, Configuration configuration )
{
if ( configuration != null )
{
RemoteRepositoryConfiguration oldCfg = configuration.findRemoteRepositoryById( remoteRepositoryConfiguration.getId( ) );
if ( oldCfg != null )
{
configuration.removeRemoteRepository( oldCfg );
}
configuration.addRemoteRepository( remoteRepositoryConfiguration );
}
}
private void replaceOrAddRepositoryConfig( RepositoryGroupConfiguration repositoryGroupConfiguration, Configuration configuration )
{
RepositoryGroupConfiguration oldCfg = configuration.findRepositoryGroupById( repositoryGroupConfiguration.getId( ) );
if ( oldCfg != null )
{
configuration.removeRepositoryGroup( oldCfg );
}
configuration.addRepositoryGroup( repositoryGroupConfiguration );
}
@Override
public RemoteRepository putRepository( RemoteRepository remoteRepository, Configuration configuration ) throws RepositoryException
{
rwLock.writeLock( ).lock( );
try
{
final String id = remoteRepository.getId( );
if ( managedRepositories.containsKey( id ) )
{
throw new RepositoryException( "There exists a managed repository with id " + id + ". Could not update with remote repository." );
}
RemoteRepository originRepo = remoteRepositories.put( id, remoteRepository );
RemoteRepositoryConfiguration oldCfg = null;
RemoteRepositoryConfiguration newCfg;
try
{
if ( originRepo != null && originRepo != remoteRepository )
{
originRepo.close( );
}
final RepositoryProvider provider = getProvider( remoteRepository.getType( ) );
newCfg = provider.getRemoteConfiguration( remoteRepository );
updateRepositoryReferences( provider, remoteRepository, newCfg, configuration );
oldCfg = configuration.findRemoteRepositoryById( id );
if ( oldCfg != null )
{
configuration.removeRemoteRepository( oldCfg );
}
configuration.addRemoteRepository( newCfg );
if ( remoteRepository != originRepo )
{
pushEvent( new LifecycleEvent( LifecycleEvent.REGISTERED, this, remoteRepository ) );
}
else
{
pushEvent( new LifecycleEvent( LifecycleEvent.UPDATED, this, remoteRepository ) );
}
return remoteRepository;
}
catch ( Exception e )
{
// Rollback
if ( originRepo != null )
{
remoteRepositories.put( id, originRepo );
}
else
{
remoteRepositories.remove( id );
}
if ( oldCfg != null )
{
RemoteRepositoryConfiguration cfg = configuration.findRemoteRepositoryById( id );
if ( cfg != null )
{
configuration.removeRemoteRepository( cfg );
configuration.addRemoteRepository( oldCfg );
}
}
log.error( "Error while adding remote repository {}", e.getMessage( ), e );
throw new RepositoryException( "Could not save the configuration" + ( e.getMessage( ) == null ? "" : ": " + e.getMessage( ) ) );
}
}
finally
{
rwLock.writeLock( ).unlock( );
}
}
/**
* Adds a remote repository, or overwrites the repository definition with the same id, if it exists already.
* The modification is saved to the configuration immediately.
*
* @param remoteRepository the remote repository to add
* @throws RepositoryException if an error occurs during configuration save
*/
@Override
public RemoteRepository putRepository( RemoteRepository remoteRepository ) throws RepositoryException
{
rwLock.writeLock( ).lock( );
try
{
Configuration configuration = configurationHandler.getBaseConfiguration( );
try
{
RemoteRepository repo = putRepository( remoteRepository, configuration );
saveConfiguration( configuration );
return repo;
}
catch ( RegistryException | IndeterminateConfigurationException e )
{
log.error( "Error while saving remote repository {}", e.getMessage( ), e );
throw new RepositoryException( "Could not save the configuration" + ( e.getMessage( ) == null ? "" : ": " + e.getMessage( ) ) );
}
}
finally
{
rwLock.writeLock( ).unlock( );
}
}
/**
* Adds a new repository or updates the repository with the same id, if it exists already.
* The configuration is saved immediately.
*
* @param remoteRepositoryConfiguration the repository configuration
* @return the updated or created repository
* @throws RepositoryException if an error occurs, or the configuration is not valid.
*/
@Override
public RemoteRepository putRepository( RemoteRepositoryConfiguration remoteRepositoryConfiguration ) throws RepositoryException
{
rwLock.writeLock( ).lock( );
try
{
final String id = remoteRepositoryConfiguration.getId( );
final RepositoryType repositoryType = RepositoryType.valueOf( remoteRepositoryConfiguration.getType( ) );
Configuration configuration = configurationHandler.getBaseConfiguration( );
RemoteRepository repo = remoteRepositories.get( id );
RemoteRepositoryConfiguration oldCfg = repo != null ? getProvider( repositoryType ).getRemoteConfiguration( repo ) : null;
repo = putRepository( remoteRepositoryConfiguration, configuration );
try
{
saveConfiguration( configuration );
}
catch ( IndeterminateConfigurationException | RegistryException e )
{
if ( oldCfg != null )
{
getProvider( repositoryType ).updateRemoteInstance( (EditableRemoteRepository) repo, oldCfg );
}
log.error( "Could not save the configuration for repository {}: {}", id, e.getMessage( ), e );
throw new RepositoryException( "Could not save the configuration for repository " + id + ": " + e.getMessage( ) );
}
return repo;
}
finally
{
rwLock.writeLock( ).unlock( );
}
}
/**
* Adds a new repository or updates the repository with the same id. The given configuration object is updated, but
* the configuration is not saved.
*
* @param remoteRepositoryConfiguration the new or changed repository configuration
* @param configuration the configuration object
* @return the new or updated repository
* @throws RepositoryException if the configuration cannot be saved or updated
*/
@Override
@SuppressWarnings( "unchecked" )
public RemoteRepository putRepository( RemoteRepositoryConfiguration remoteRepositoryConfiguration, Configuration configuration ) throws RepositoryException
{
rwLock.writeLock( ).lock( );
try
{
final String id = remoteRepositoryConfiguration.getId( );
final RepositoryType repoType = RepositoryType.valueOf( remoteRepositoryConfiguration.getType( ) );
RemoteRepository repo;
boolean registeredNew = false;
repo = remoteRepositories.get( id );
if ( repo != null && repo.isOpen( ) )
{
if ( repo instanceof EditableRemoteRepository )
{
getProvider( repoType ).updateRemoteInstance( (EditableRemoteRepository) repo, remoteRepositoryConfiguration );
}
else
{
throw new RepositoryException( "The repository is not editable " + id );
}
}
else
{
repo = getProvider( repoType ).createRemoteInstance( remoteRepositoryConfiguration );
remoteRepositories.put( id, repo );
registeredNew = true;
}
updateRepositoryReferences( getProvider( repoType ), repo, remoteRepositoryConfiguration, configuration );
replaceOrAddRepositoryConfig( remoteRepositoryConfiguration, configuration );
if ( registeredNew )
{
pushEvent( new LifecycleEvent( LifecycleEvent.REGISTERED, this, repo ) );
}
else
{
pushEvent( new LifecycleEvent( LifecycleEvent.UPDATED, this, repo ) );
}
return repo;
}
finally
{
rwLock.writeLock( ).unlock( );
}
}
@Override
public void removeRepository( String repoId ) throws RepositoryException
{
Repository repo = getRepository( repoId );
if ( repo != null )
{
removeRepository( repo );
}
}
@Override
public void removeRepository( Repository repo ) throws RepositoryException
{
if ( repo == null )
{
log.warn( "Trying to remove null repository" );
return;
}
if ( repo instanceof RemoteRepository )
{
removeRepository( (RemoteRepository) repo );
}
else if ( repo instanceof ManagedRepository )
{
removeRepository( (ManagedRepository) repo );
}
else if ( repo instanceof RepositoryGroup )
{
removeRepositoryGroup( (RepositoryGroup) repo );
}
else
{
throw new RepositoryException( "Repository type not known: " + repo.getClass( ) );
}
}
/**
* Removes a managed repository from the registry and configuration, if it exists.
* The change is saved to the configuration immediately.
*
* @param managedRepository the managed repository to remove
* @throws RepositoryException if a error occurs during configuration save
*/
@Override
public void removeRepository( ManagedRepository managedRepository ) throws RepositoryException
{
if ( managedRepository == null )
{
return;
}
final String id = managedRepository.getId( );
ManagedRepository repo = getManagedRepository( id );
if ( repo != null )
{
rwLock.writeLock( ).lock( );
try
{
repo = managedRepositories.remove( id );
if ( repo != null )
{
repo.close( );
this.groupHandler.removeRepositoryFromGroups( repo );
Configuration configuration = configurationHandler.getBaseConfiguration( );
ManagedRepositoryConfiguration cfg = configuration.findManagedRepositoryById( id );
if ( cfg != null )
{
configuration.removeManagedRepository( cfg );
}
saveConfiguration( configuration );
}
pushEvent( new LifecycleEvent( LifecycleEvent.UNREGISTERED, this, repo ) );
}
catch ( RegistryException | IndeterminateConfigurationException e )
{
// Rollback
log.error( "Could not save config after repository removal: {}", e.getMessage( ), e );
managedRepositories.put( repo.getId( ), repo );
throw new RepositoryException( "Could not save configuration after repository removal: " + e.getMessage( ) );
}
finally
{
rwLock.writeLock( ).unlock( );
}
}
}
@Override
public void removeRepository( ManagedRepository managedRepository, Configuration configuration ) throws RepositoryException
{
if ( managedRepository == null )
{
return;
}
final String id = managedRepository.getId( );
ManagedRepository repo = getManagedRepository( id );
if ( repo != null )
{
rwLock.writeLock( ).lock( );
try
{
repo = managedRepositories.remove( id );
if ( repo != null )
{
repo.close( );
this.groupHandler.removeRepositoryFromGroups( repo );
ManagedRepositoryConfiguration cfg = configuration.findManagedRepositoryById( id );
if ( cfg != null )
{
configuration.removeManagedRepository( cfg );
}
}
pushEvent( new LifecycleEvent( LifecycleEvent.UNREGISTERED, this, repo ) );
}
finally
{
rwLock.writeLock( ).unlock( );
}
}
}
/**
* Removes a repository group from the registry and configuration, if it exists.
* The change is saved to the configuration immediately.
*
* @param repositoryGroup the repository group to remove
* @throws RepositoryException if a error occurs during configuration save
*/
@Override
public void removeRepositoryGroup( RepositoryGroup repositoryGroup ) throws RepositoryException
{
if ( repositoryGroup == null )
{
return;
}
final String id = repositoryGroup.getId( );
if ( groupHandler.has( id ) )
{
rwLock.writeLock( ).lock( );
try
{
groupHandler.remove( id );
}
finally
{
rwLock.writeLock( ).unlock( );
}
}
}
@Override
public void removeRepositoryGroup( RepositoryGroup repositoryGroup, Configuration configuration ) throws RepositoryException
{
if ( repositoryGroup == null )
{
return;
}
final String id = repositoryGroup.getId( );
if ( groupHandler.has( id ) )
{
rwLock.writeLock( ).lock( );
try
{
groupHandler.remove( id, configuration );
}
finally
{
rwLock.writeLock( ).unlock( );
}
}
}
private void doRemoveRepo( RemoteRepository repo, Configuration configuration )
{
repo.close( );
RemoteRepositoryConfiguration cfg = configuration.findRemoteRepositoryById( repo.getId( ) );
if ( cfg != null )
{
configuration.removeRemoteRepository( cfg );
}
List