blob: 4bd19998c11cd91bdbe578e71ded7cf0186550b1 [file] [log] [blame]
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.configuration.*;
import org.apache.archiva.event.Event;
import org.apache.archiva.event.EventManager;
import org.apache.archiva.event.EventType;
import org.apache.archiva.indexer.*;
import org.apache.archiva.components.registry.RegistryException;
import org.apache.archiva.repository.EditableManagedRepository;
import org.apache.archiva.repository.EditableRemoteRepository;
import org.apache.archiva.repository.EditableRepository;
import org.apache.archiva.repository.EditableRepositoryGroup;
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.*;
import org.apache.archiva.event.EventHandler;
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.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.*;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.apache.archiva.indexer.ArchivaIndexManager.DEFAULT_INDEX_PATH;
/**
* Registry for repositories. This is the central entry point for repositories. It provides methods for
* retrieving, adding and removing repositories.
* <p>
* The modification methods addXX and removeXX persist the changes immediately to the configuration. If the
* configuration save fails the changes are rolled back.
* <p>
* TODO: Audit events
*
* @since 3.0
*/
@Service("repositoryRegistry")
public class ArchivaRepositoryRegistry implements ConfigurationListener, EventHandler<Event>,
RepositoryRegistry
{
private static final Logger log = LoggerFactory.getLogger(RepositoryRegistry.class);
/**
* We inject all repository providers
*/
@Inject
List<RepositoryProvider> repositoryProviders;
@Inject
IndexManagerFactory indexManagerFactory;
@Inject
ArchivaConfiguration archivaConfiguration;
@Inject
List<MetadataReader> metadataReaderList;
@Inject
@Named("repositoryContentFactory#default")
RepositoryContentFactory repositoryContentFactory;
private final EventManager eventManager;
private Map<String, ManagedRepository> managedRepositories = new HashMap<>();
private Map<String, ManagedRepository> uManagedRepository = Collections.unmodifiableMap(managedRepositories);
private Map<String, RemoteRepository> remoteRepositories = new HashMap<>();
private Map<String, RemoteRepository> uRemoteRepositories = Collections.unmodifiableMap(remoteRepositories);
private Map<String, RepositoryGroup> repositoryGroups = new HashMap<>();
private Map<String, RepositoryGroup> uRepositoryGroups = Collections.unmodifiableMap(repositoryGroups);
private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private volatile boolean ignoreConfigEvents = false;
public ArchivaRepositoryRegistry() {
this.eventManager = new EventManager(this);
}
@Override
public void setArchivaConfiguration( ArchivaConfiguration archivaConfiguration ) {
this.archivaConfiguration = archivaConfiguration;
}
@PostConstruct
private void initialize() {
rwLock.writeLock().lock();
try {
log.debug("Initializing repository registry");
updateManagedRepositoriesFromConfig();
updateRemoteRepositoriesFromConfig();
repositoryGroups.clear();
Map<String, RepositoryGroup> repositoryGroups = getRepositorGroupsFromConfig();
this.repositoryGroups.putAll(repositoryGroups);
// archivaConfiguration.addChangeListener(this);
archivaConfiguration.addListener(this);
} finally {
rwLock.writeLock().unlock();
}
pushEvent(new RepositoryRegistryEvent(RepositoryRegistryEvent.RELOADED, this));
}
@PreDestroy
public void destroy() {
for (ManagedRepository rep : managedRepositories.values()) {
rep.close();
}
managedRepositories.clear();
for (RemoteRepository repo : remoteRepositories.values()) {
repo.close();
}
remoteRepositories.clear();
pushEvent(new RepositoryRegistryEvent(RepositoryRegistryEvent.DESTROYED, this));
}
private Map<RepositoryType, RepositoryProvider> createProviderMap() {
Map<RepositoryType, RepositoryProvider> map = new HashMap<>();
if (repositoryProviders != null) {
for (RepositoryProvider provider : repositoryProviders) {
for (RepositoryType type : provider.provides()) {
map.put(type, provider);
}
}
}
return map;
}
private RepositoryProvider getProvider(RepositoryType type) throws RepositoryException
{
return repositoryProviders.stream().filter(repositoryProvider -> repositoryProvider.provides().contains(type)).findFirst().orElseThrow(() -> new RepositoryException("Repository type cannot be handled: " + type));
}
/*
* Updates the repositories
*/
private void updateManagedRepositoriesFromConfig() {
try {
Set<String> configRepoIds = new HashSet<>();
List<ManagedRepositoryConfiguration> managedRepoConfigs =
getArchivaConfiguration().getConfiguration().getManagedRepositories();
if (managedRepoConfigs == null) {
return;
}
for (ManagedRepositoryConfiguration repoConfig : managedRepoConfigs) {
ManagedRepository repo = putRepository(repoConfig, null);
configRepoIds.add(repoConfig.getId());
if (repo.supportsFeature(StagingRepositoryFeature.class)) {
StagingRepositoryFeature stagF = repo.getFeature(StagingRepositoryFeature.class).get();
if (stagF.getStagingRepository() != null) {
configRepoIds.add(stagF.getStagingRepository().getId());
}
}
}
List<String> toRemove = managedRepositories.keySet().stream().filter(id -> !configRepoIds.contains(id)).collect(Collectors.toList());
for (String id : toRemove) {
ManagedRepository removed = managedRepositories.remove(id);
removed.close();
}
} catch (Throwable e) {
log.error("Could not initialize repositories from config: {}", e.getMessage(), e);
return;
}
}
private ManagedRepository createNewManagedRepository(RepositoryProvider provider, ManagedRepositoryConfiguration cfg) throws RepositoryException {
log.debug("Creating repo {}", cfg.getId());
ManagedRepository repo = provider.createManagedInstance(cfg);
repo.registerEventHandler(RepositoryEvent.ANY, this);
updateRepositoryReferences(provider, repo, cfg, null);
return repo;
}
private String getStagingId(String repoId) {
return repoId + StagingRepositoryFeature.STAGING_REPO_POSTFIX;
}
@SuppressWarnings("unchecked")
private void updateRepositoryReferences(RepositoryProvider provider, ManagedRepository repo, ManagedRepositoryConfiguration cfg, Configuration configuration) throws RepositoryException {
log.debug("Updating references of repo {}", repo.getId());
if (repo.supportsFeature(StagingRepositoryFeature.class)) {
StagingRepositoryFeature feature = repo.getFeature(StagingRepositoryFeature.class).get();
if (feature.isStageRepoNeeded() && feature.getStagingRepository() == null) {
ManagedRepository stageRepo = getManagedRepository(getStagingId(repo.getId()));
if (stageRepo == null) {
stageRepo = getStagingRepository(provider, cfg, configuration);
managedRepositories.put(stageRepo.getId(), stageRepo);
if (configuration != null) {
replaceOrAddRepositoryConfig(provider.getManagedConfiguration(stageRepo), configuration);
}
pushEvent(new LifecycleEvent(LifecycleEvent.REGISTERED, this, stageRepo));
}
feature.setStagingRepository(stageRepo);
}
}
if (repo instanceof EditableManagedRepository ) {
EditableManagedRepository editableRepo = (EditableManagedRepository) repo;
if (repo.getContent() == null) {
editableRepo.setContent(repositoryContentFactory.getManagedRepositoryContent(repo));
editableRepo.getContent().setRepository(editableRepo);
}
log.debug("Index repo: " + repo.hasIndex());
if (repo.hasIndex() && ( repo.getIndexingContext() == null || !repo.getIndexingContext().isOpen() )) {
log.debug("Creating indexing context for {}", repo.getId());
createIndexingContext(editableRepo);
}
}
repo.registerEventHandler(RepositoryEvent.ANY, this);
}
@Override
public ArchivaIndexManager getIndexManager( RepositoryType type ) {
return indexManagerFactory.getIndexManager(type);
}
@Override
public MetadataReader getMetadataReader( final RepositoryType type ) throws UnsupportedRepositoryTypeException
{
if (metadataReaderList!=null) {
return metadataReaderList.stream( ).filter( mr -> mr.isValidForType( type ) ).findFirst( ).orElseThrow( ( ) -> new UnsupportedRepositoryTypeException( type ) );
} else {
throw new UnsupportedRepositoryTypeException( type );
}
}
private void createIndexingContext( EditableRepository editableRepo) throws RepositoryException {
if (editableRepo.supportsFeature(IndexCreationFeature.class)) {
ArchivaIndexManager idxManager = getIndexManager(editableRepo.getType());
try {
editableRepo.setIndexingContext(idxManager.createContext(editableRepo));
idxManager.updateLocalIndexPath(editableRepo);
} catch (IndexCreationFailedException e) {
throw new RepositoryException("Could not create index for repository " + editableRepo.getId() + ": " + e.getMessage(), e);
}
}
}
private ManagedRepository getStagingRepository(RepositoryProvider provider, ManagedRepositoryConfiguration baseRepoCfg, Configuration configuration) throws RepositoryException {
ManagedRepository stageRepo = getManagedRepository(getStagingId(baseRepoCfg.getId()));
if (stageRepo == null) {
stageRepo = provider.createStagingInstance(baseRepoCfg);
if (stageRepo.supportsFeature(StagingRepositoryFeature.class)) {
stageRepo.getFeature(StagingRepositoryFeature.class).get().setStageRepoNeeded(false);
}
ManagedRepositoryConfiguration stageCfg = provider.getManagedConfiguration(stageRepo);
updateRepositoryReferences(provider, stageRepo, stageCfg, configuration);
}
return stageRepo;
}
private void updateRemoteRepositoriesFromConfig() {
try {
List<RemoteRepositoryConfiguration> remoteRepoConfigs =
getArchivaConfiguration().getConfiguration().getRemoteRepositories();
if (remoteRepoConfigs == null) {
return;
}
Set<String> repoIds = new HashSet<>();
for (RemoteRepositoryConfiguration repoConfig : remoteRepoConfigs) {
putRepository(repoConfig, null);
repoIds.add(repoConfig.getId());
}
List<String> toRemove = remoteRepositories.keySet().stream().filter(id -> !repoIds.contains(id)).collect(Collectors.toList());
for (String id : toRemove) {
RemoteRepository removed = remoteRepositories.remove(id);
removed.close();
}
} catch (Throwable e) {
log.error("Could not initialize remote repositories from config: {}", e.getMessage(), e);
return;
}
}
private RemoteRepository createNewRemoteRepository(RepositoryProvider provider, RemoteRepositoryConfiguration cfg) throws RepositoryException {
log.debug("Creating remote repo {}", cfg.getId());
RemoteRepository repo = provider.createRemoteInstance(cfg);
updateRepositoryReferences(provider, repo, cfg, null);
return repo;
}
private void updateRepositoryReferences(RepositoryProvider provider, RemoteRepository repo, RemoteRepositoryConfiguration cfg, Configuration configuration) throws RepositoryException {
if (repo instanceof EditableRemoteRepository && repo.getContent() == null) {
EditableRemoteRepository editableRepo = (EditableRemoteRepository) repo;
editableRepo.setContent(repositoryContentFactory.getRemoteRepositoryContent(repo));
if (repo.supportsFeature(IndexCreationFeature.class) && repo.getIndexingContext() == null) {
createIndexingContext(editableRepo);
}
}
repo.registerEventHandler(RepositoryEvent.ANY, this);
}
private Map<String, RepositoryGroup> getRepositorGroupsFromConfig() {
try {
List<RepositoryGroupConfiguration> repositoryGroupConfigurations =
getArchivaConfiguration().getConfiguration().getRepositoryGroups();
if (repositoryGroupConfigurations == null) {
return Collections.emptyMap();
}
Map<String, RepositoryGroup> repositoryGroupMap = new LinkedHashMap<>(repositoryGroupConfigurations.size());
Map<RepositoryType, RepositoryProvider> providerMap = createProviderMap();
for (RepositoryGroupConfiguration repoConfig : repositoryGroupConfigurations) {
RepositoryType repositoryType = RepositoryType.valueOf(repoConfig.getType());
if (providerMap.containsKey(repositoryType)) {
try {
RepositoryGroup repo = createNewRepositoryGroup(providerMap.get(repositoryType), repoConfig);
repositoryGroupMap.put(repo.getId(), repo);
} catch (Exception e) {
log.error("Could not create repository group {}: {}", repoConfig.getId(), e.getMessage(), e);
}
}
}
return repositoryGroupMap;
} catch (Throwable e) {
log.error("Could not initialize repositories from config: {}", e.getMessage(), e);
return Collections.emptyMap();
}
}
private RepositoryGroup createNewRepositoryGroup(RepositoryProvider provider, RepositoryGroupConfiguration config) throws RepositoryException {
RepositoryGroup repositoryGroup = provider.createRepositoryGroup(config);
repositoryGroup.registerEventHandler(RepositoryEvent.ANY, this);
updateRepositoryReferences(provider, repositoryGroup, config);
return repositoryGroup;
}
private void updateRepositoryReferences(RepositoryProvider provider, RepositoryGroup group, RepositoryGroupConfiguration configuration) {
if (group instanceof EditableRepositoryGroup ) {
EditableRepositoryGroup eGroup = (EditableRepositoryGroup) group;
eGroup.setRepositories(configuration.getRepositories().stream().map(r -> getManagedRepository(r)).collect(Collectors.toList()));
}
}
private ArchivaConfiguration getArchivaConfiguration() {
return this.archivaConfiguration;
}
/**
* Returns all repositories that are registered. There is no defined order of the returned repositories.
*
* @return a list of managed and remote repositories
*/
@Override
public Collection<Repository> getRepositories( ) {
rwLock.readLock().lock();
try {
return Stream.concat(managedRepositories.values().stream(), remoteRepositories.values().stream()).collect(Collectors.toList());
} finally {
rwLock.readLock().unlock();
}
}
/**
* Returns only the managed repositories. There is no defined order of the returned repositories.
*
* @return a list of managed repositories
*/
@Override
public Collection<ManagedRepository> getManagedRepositories( ) {
rwLock.readLock().lock();
try {
return uManagedRepository.values();
} finally {
rwLock.readLock().unlock();
}
}
/**
* Returns only the remote repositories. There is no defined order of the returned repositories.
*
* @return a list of remote repositories
*/
@Override
public Collection<RemoteRepository> getRemoteRepositories( ) {
rwLock.readLock().lock();
try {
return uRemoteRepositories.values();
} finally {
rwLock.readLock().unlock();
}
}
@Override
public Collection<RepositoryGroup> getRepositoryGroups( ) {
rwLock.readLock().lock();
try {
return uRepositoryGroups.values();
} finally {
rwLock.readLock().unlock();
}
}
/**
* Returns the repository with the given id. The returned repository may be a managed or remote repository.
* It returns null, if no repository is registered with the given id.
*
* @param repoId the repository id
* @return the repository if found, otherwise null
*/
@Override
public Repository getRepository( String repoId ) {
rwLock.readLock().lock();
try {
log.debug("getRepository {}", repoId);
if (managedRepositories.containsKey(repoId)) {
log.debug("Managed repo");
return managedRepositories.get(repoId);
} else if (remoteRepositories.containsKey(repoId)) {
log.debug("Remote repo");
return remoteRepositories.get(repoId);
} else if (repositoryGroups.containsKey(repoId)) {
return repositoryGroups.get(repoId);
} else {
return null;
}
} finally {
rwLock.readLock().unlock();
}
}
/**
* Convenience method, that returns the managed repository with the given id.
* It returns null, if no managed repository is registered with this id.
*
* @param repoId the repository id
* @return the managed repository if found, otherwise null
*/
@Override
public ManagedRepository getManagedRepository( String repoId ) {
rwLock.readLock().lock();
try {
return managedRepositories.get(repoId);
} finally {
rwLock.readLock().unlock();
}
}
/**
* Convenience method, that returns the remote repository with the given id.
* It returns null, if no remote repository is registered with this id.
*
* @param repoId the repository id
* @return the remote repository if found, otherwise null
*/
@Override
public RemoteRepository getRemoteRepository( String repoId ) {
rwLock.readLock().lock();
try {
return remoteRepositories.get(repoId);
} finally {
rwLock.readLock().unlock();
}
}
@Override
public RepositoryGroup getRepositoryGroup( String groupId ) {
rwLock.readLock().lock();
try {
return repositoryGroups.get(groupId);
} finally {
rwLock.readLock().unlock();
}
}
/*
* The <code>ignoreConfigEvents</code> works only for synchronized configuration events.
* If the configuration throws async events, we cannot know, if the event is caused by this instance or another thread.
*/
private void saveConfiguration(Configuration configuration) throws IndeterminateConfigurationException, RegistryException {
ignoreConfigEvents = true;
try {
getArchivaConfiguration().save(configuration);
} finally {
ignoreConfigEvents = false;
}
}
/**
* Adds a new repository to the current list, or replaces the repository definition with
* the same id, if it exists already.
* The change is saved to the configuration immediately.
*
* @param managedRepository the new repository.
* @throws RepositoryException if the new repository could not be saved to the configuration.
*/
@Override
public ManagedRepository putRepository( ManagedRepository managedRepository ) throws RepositoryException {
rwLock.writeLock().lock();
try {
final String id = managedRepository.getId();
if (remoteRepositories.containsKey(id)) {
throw new RepositoryException("There exists a remote repository with id " + id + ". Could not update with managed repository.");
}
ManagedRepository originRepo = managedRepositories.put(id, managedRepository);
try {
if (originRepo != null && originRepo != managedRepository) {
originRepo.close();
}
RepositoryProvider provider = getProvider(managedRepository.getType());
ManagedRepositoryConfiguration newCfg = provider.getManagedConfiguration(managedRepository);
Configuration configuration = getArchivaConfiguration().getConfiguration();
updateRepositoryReferences(provider, managedRepository, newCfg, configuration);
ManagedRepositoryConfiguration oldCfg = configuration.findManagedRepositoryById(id);
if (oldCfg != null) {
configuration.removeManagedRepository(oldCfg);
}
configuration.addManagedRepository(newCfg);
saveConfiguration(configuration);
if (originRepo != managedRepository) {
pushEvent(new LifecycleEvent(LifecycleEvent.REGISTERED, this, managedRepository));
} else {
pushEvent(new LifecycleEvent(LifecycleEvent.UPDATED, this, managedRepository));
}
return managedRepository;
} catch (Exception e) {
// Rollback only partly, because repository is closed already
if (originRepo != null) {
managedRepositories.put(id, originRepo);
} else {
managedRepositories.remove(id);
}
log.error("Exception during configuration update {}", 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 managedRepositoryConfiguration the repository configuration
* @return the updated or created repository
* @throws RepositoryException if an error occurs, or the configuration is not valid.
*/
@Override
public ManagedRepository putRepository( ManagedRepositoryConfiguration managedRepositoryConfiguration ) throws RepositoryException {
rwLock.writeLock().lock();
try {
final String id = managedRepositoryConfiguration.getId();
final RepositoryType repositoryType = RepositoryType.valueOf(managedRepositoryConfiguration.getType());
Configuration configuration = getArchivaConfiguration().getConfiguration();
ManagedRepository repo = managedRepositories.get(id);
ManagedRepositoryConfiguration oldCfg = repo != null ? getProvider(repositoryType).getManagedConfiguration(repo) : null;
repo = putRepository(managedRepositoryConfiguration, configuration);
try {
saveConfiguration(configuration);
} catch (IndeterminateConfigurationException | RegistryException e) {
if (oldCfg != null) {
getProvider(repositoryType).updateManagedInstance((EditableManagedRepository) 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 managedRepositoryConfiguration the new or changed managed repository configuration
* @param configuration the configuration object (may be <code>null</code>)
* @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 {
final String id = repositoryGroup.getId();
RepositoryGroup originRepoGroup = repositoryGroups.put(id, repositoryGroup);
try {
if (originRepoGroup != null && originRepoGroup != repositoryGroup) {
originRepoGroup.close();
}
RepositoryProvider provider = getProvider(repositoryGroup.getType());
RepositoryGroupConfiguration newCfg = provider.getRepositoryGroupConfiguration(repositoryGroup);
Configuration configuration = getArchivaConfiguration().getConfiguration();
updateRepositoryReferences(provider, repositoryGroup, newCfg);
RepositoryGroupConfiguration oldCfg = configuration.findRepositoryGroupById(id);
if (oldCfg != null) {
configuration.removeRepositoryGroup(oldCfg);
}
configuration.addRepositoryGroup(newCfg);
saveConfiguration(configuration);
return repositoryGroup;
} catch (Exception e) {
// Rollback
if (originRepoGroup != null) {
repositoryGroups.put(id, originRepoGroup);
} else {
repositoryGroups.remove(id);
}
log.error("Exception during configuration update {}", e.getMessage(), e);
throw new RepositoryException("Could not save the configuration" + (e.getMessage() == null ? "" : ": " + e.getMessage()));
}
} 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 {
final String id = repositoryGroupConfiguration.getId();
final RepositoryType repositoryType = RepositoryType.valueOf(repositoryGroupConfiguration.getType());
Configuration configuration = getArchivaConfiguration().getConfiguration();
RepositoryGroup repo = repositoryGroups.get(id);
RepositoryGroupConfiguration oldCfg = repo != null ? getProvider(repositoryType).getRepositoryGroupConfiguration(repo) : null;
repo = putRepositoryGroup(repositoryGroupConfiguration, configuration);
try {
saveConfiguration(configuration);
} catch (IndeterminateConfigurationException | RegistryException e) {
if (oldCfg != null) {
getProvider(repositoryType).updateRepositoryGroupInstance((EditableRepositoryGroup) repo, oldCfg);
}
log.error("Could not save the configuration for repository group {}: {}", id, e.getMessage(), e);
throw new RepositoryException("Could not save the configuration for repository group " + id + ": " + e.getMessage());
}
return repo;
} finally {
rwLock.writeLock().unlock();
}
}
/**
* Adds a new repository group or updates the repository group with the same id. The given configuration object is updated, but
* the configuration is not saved.
*
* @param repositoryGroupConfiguration The configuration of the new or changed repository group.
* @param configuration The configuration object. If it is <code>null</code>, 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 {
final String id = repositoryGroupConfiguration.getId();
final RepositoryType repoType = RepositoryType.valueOf(repositoryGroupConfiguration.getType());
RepositoryGroup repo;
setRepositoryGroupDefaults(repositoryGroupConfiguration);
if (repositoryGroups.containsKey(id)) {
repo = repositoryGroups.get(id);
if (repo instanceof EditableRepositoryGroup) {
getProvider(repoType).updateRepositoryGroupInstance((EditableRepositoryGroup) repo, repositoryGroupConfiguration);
} else {
throw new RepositoryException("The repository is not editable " + id);
}
} else {
repo = getProvider(repoType).createRepositoryGroup(repositoryGroupConfiguration);
repositoryGroups.put(id, repo);
}
updateRepositoryReferences(getProvider(repoType), repo, repositoryGroupConfiguration);
replaceOrAddRepositoryConfig(repositoryGroupConfiguration, configuration);
return repo;
} finally {
rwLock.writeLock().unlock();
}
}
private void setRepositoryGroupDefaults(RepositoryGroupConfiguration repositoryGroupConfiguration) {
if (StringUtils.isEmpty(repositoryGroupConfiguration.getMergedIndexPath())) {
repositoryGroupConfiguration.setMergedIndexPath(DEFAULT_INDEX_PATH);
}
if (repositoryGroupConfiguration.getMergedIndexTtl() <= 0) {
repositoryGroupConfiguration.setMergedIndexTtl(300);
}
if (StringUtils.isEmpty(repositoryGroupConfiguration.getCronExpression())) {
repositoryGroupConfiguration.setCronExpression("0 0 03 ? * MON");
}
}
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 = getArchivaConfiguration().getConfiguration();
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 = getArchivaConfiguration().getConfiguration();
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();
removeRepositoryFromGroups(repo);
Configuration configuration = getArchivaConfiguration().getConfiguration();
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();
}
}
}
private void removeRepositoryFromGroups(ManagedRepository repo) {
if (repo != null) {
repositoryGroups.values().stream().filter(repoGroup -> repoGroup instanceof EditableRepository).
map(repoGroup -> (EditableRepositoryGroup) repoGroup).forEach(repoGroup -> repoGroup.removeRepository(repo));
}
}
@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();
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();
RepositoryGroup repo = getRepositoryGroup(id);
if (repo != null) {
rwLock.writeLock().lock();
try {
repo = repositoryGroups.remove(id);
if (repo != null) {
repo.close();
Configuration configuration = getArchivaConfiguration().getConfiguration();
RepositoryGroupConfiguration cfg = configuration.findRepositoryGroupById(id);
if (cfg != null) {
configuration.removeRepositoryGroup(cfg);
}
saveConfiguration(configuration);
}
} catch (RegistryException | IndeterminateConfigurationException e) {
// Rollback
log.error("Could not save config after repository removal: {}", e.getMessage(), e);
repositoryGroups.put(repo.getId(), repo);
throw new RepositoryException("Could not save configuration after repository removal: " + e.getMessage());
} finally {
rwLock.writeLock().unlock();
}
}
}
@Override
public void removeRepositoryGroup( RepositoryGroup repositoryGroup, Configuration configuration ) throws RepositoryException {
if (repositoryGroup == null) {
return;
}
final String id = repositoryGroup.getId();
RepositoryGroup repo = getRepositoryGroup(id);
if (repo != null) {
rwLock.writeLock().lock();
try {
repo = repositoryGroups.remove(id);
if (repo != null) {
repo.close();
RepositoryGroupConfiguration cfg = configuration.findRepositoryGroupById(id);
if (cfg != null) {
configuration.removeRepositoryGroup(cfg);
}
}
} 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<ProxyConnectorConfiguration> proxyConnectors = new ArrayList<>(configuration.getProxyConnectors());
for (ProxyConnectorConfiguration proxyConnector : proxyConnectors) {
if (StringUtils.equals(proxyConnector.getTargetRepoId(), repo.getId())) {
configuration.removeProxyConnector(proxyConnector);
}
}
}
/**
* Removes the remote repository from the registry and configuration.
* The change is saved to the configuration immediately.
*
* @param remoteRepository the remote repository to remove
* @throws RepositoryException if a error occurs during configuration save
*/
@Override
public void removeRepository( RemoteRepository remoteRepository ) throws RepositoryException {
if (remoteRepository == null) {
return;
}
final String id = remoteRepository.getId();
RemoteRepository repo = getRemoteRepository(id);
if (repo != null) {
rwLock.writeLock().lock();
try {
repo = remoteRepositories.remove(id);
if (repo != null) {
Configuration configuration = getArchivaConfiguration().getConfiguration();
doRemoveRepo(repo, configuration);
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);
remoteRepositories.put(repo.getId(), repo);
throw new RepositoryException("Could not save configuration after repository removal: " + e.getMessage());
} finally {
rwLock.writeLock().unlock();
}
}
}
@Override
public void removeRepository( RemoteRepository remoteRepository, Configuration configuration ) throws RepositoryException {
if (remoteRepository == null) {
return;
}
final String id = remoteRepository.getId();
RemoteRepository repo = getRemoteRepository(id);
if (repo != null) {
rwLock.writeLock().lock();
try {
repo = remoteRepositories.remove(id);
if (repo != null) {
doRemoveRepo(repo, configuration);
}
pushEvent(new LifecycleEvent(LifecycleEvent.UNREGISTERED, this, repo));
} finally {
rwLock.writeLock().unlock();
}
}
}
/**
* Reloads the registry from the configuration.
*/
@Override
public void reload( ) {
initialize();
}
/**
* Resets the indexing context of a given repository.
*
* @param repository The repository
* @throws IndexUpdateFailedException If the index could not be resetted.
*/
@Override
public void resetIndexingContext( Repository repository ) throws IndexUpdateFailedException {
if (repository.hasIndex() && repository instanceof EditableRepository) {
EditableRepository eRepo = (EditableRepository) repository;
ArchivaIndexingContext newCtx = getIndexManager(repository.getType()).reset(repository.getIndexingContext());
eRepo.setIndexingContext(newCtx);
}
}
/**
* Creates a new repository instance with the same settings as this one. The cloned repository is not
* registered or saved to the configuration.
*
* @param repo The origin repository
* @return The cloned repository.
*/
@Override
public ManagedRepository clone( ManagedRepository repo, String newId ) throws RepositoryException {
if (managedRepositories.containsKey(newId) || remoteRepositories.containsKey(newId)) {
throw new RepositoryException("The given id exists already " + newId);
}
RepositoryProvider provider = getProvider(repo.getType());
ManagedRepositoryConfiguration cfg = provider.getManagedConfiguration(repo);
cfg.setId(newId);
ManagedRepository cloned = provider.createManagedInstance(cfg);
cloned.registerEventHandler(RepositoryEvent.ANY, this);
return cloned;
}
@Override
public <T extends Repository> Repository clone( T repo, String newId ) throws RepositoryException {
if (repo instanceof RemoteRepository) {
return this.clone((RemoteRepository) repo, newId);
} else if (repo instanceof ManagedRepository) {
return this.clone((ManagedRepository) repo, newId);
} else {
throw new RepositoryException("This repository class is not supported " + repo.getClass().getName());
}
}
/**
* Creates a new repository instance with the same settings as this one. The cloned repository is not
* registered or saved to the configuration.
*
* @param repo The origin repository
* @return The cloned repository.
*/
@Override
public RemoteRepository clone( RemoteRepository repo, String newId ) throws RepositoryException {
if (managedRepositories.containsKey(newId) || remoteRepositories.containsKey(newId)) {
throw new RepositoryException("The given id exists already " + newId);
}
RepositoryProvider provider = getProvider(repo.getType());
RemoteRepositoryConfiguration cfg = provider.getRemoteConfiguration(repo);
cfg.setId(newId);
RemoteRepository cloned = provider.createRemoteInstance(cfg);
cloned.registerEventHandler(RepositoryEvent.ANY, this);
return cloned;
}
@Override
public Repository getRepositoryOfAsset( StorageAsset asset )
{
if (asset instanceof Repository) {
return (Repository)asset;
} else
{
return getRepositories( ).stream( ).filter( r -> r.getAsset( "" )
.getStorage( ).equals( asset.getStorage( ) ) ).findFirst( ).orElse( null );
}
}
@Override
public void configurationEvent(ConfigurationEvent event) {
// Note: the ignoreConfigEvents flag does not work, if the config events are asynchronous.
if (!ignoreConfigEvents) {
reload();
}
}
@Override
public <T extends Event> void registerEventHandler( EventType<T> type, EventHandler<? super T> eventHandler) {
eventManager.registerEventHandler(type, eventHandler);
}
@Override
public <T extends Event> void unregisterEventHandler(EventType<T> type, EventHandler<? super T> eventHandler) {
eventManager.unregisterEventHandler(type, eventHandler);
}
@Override
public void handle(Event event) {
// To avoid event cycles:
if (sameOriginator(event)) {
return;
}
if (event instanceof RepositoryIndexEvent) {
handleIndexCreationEvent((RepositoryIndexEvent) event);
}
// We propagate all events to our listeners, but with context of repository registry
pushEvent(event);
}
private void handleIndexCreationEvent(RepositoryIndexEvent event) {
RepositoryIndexEvent idxEvent = event;
if (managedRepositories.containsKey(idxEvent.getRepository().getId()) ||
remoteRepositories.containsKey(idxEvent.getRepository().getId())) {
EditableRepository repo = (EditableRepository) idxEvent.getRepository();
if (repo != null && repo.getIndexingContext() != null) {
try {
ArchivaIndexManager idxmgr = getIndexManager(repo.getType());
if (idxmgr != null) {
ArchivaIndexingContext newCtx = idxmgr.move(repo.getIndexingContext(), repo);
repo.setIndexingContext(newCtx);
idxmgr.updateLocalIndexPath(repo);
}
} catch (IndexCreationFailedException e) {
log.error("Could not move index to new directory {}", e.getMessage(), e);
}
}
}
}
private boolean sameOriginator(Event event) {
if (event.getSource() == this) {
return true;
} else if (event.hasPreviousEvent()) {
return sameOriginator(event.getPreviousEvent());
} else {
return false;
}
}
private void pushEvent(Event event) {
eventManager.fireEvent(event);
}
}