]> source.dussan.org Git - archiva.git/blob
28ffc5a0176111936f92e8128dde2792faf2ce42
[archiva.git] /
1 package org.apache.archiva.repository;
2
3 /*
4  * Licensed to the Apache Software Foundation (ASF) under one
5  * or more contributor license agreements.  See the NOTICE file
6  * distributed with this work for additional information
7  * regarding copyright ownership.  The ASF licenses this file
8  * to you under the Apache License, Version 2.0 (the
9  * "License"); you may not use this file except in compliance
10  * with the License.  You may obtain a copy of the License at
11  *
12  *  http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  * KIND, either express or implied.  See the License for the
18  * specific language governing permissions and limitations
19  * under the License.
20  */
21
22 import org.apache.archiva.configuration.*;
23 import org.apache.archiva.indexer.*;
24 import org.apache.archiva.redback.components.registry.RegistryException;
25 import org.apache.archiva.repository.events.*;
26 import org.apache.archiva.repository.features.IndexCreationFeature;
27 import org.apache.archiva.repository.features.StagingRepositoryFeature;
28 import org.apache.commons.lang3.StringUtils;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31 import org.springframework.stereotype.Service;
32
33 import javax.annotation.PostConstruct;
34 import javax.annotation.PreDestroy;
35 import javax.inject.Inject;
36 import javax.inject.Named;
37 import java.util.*;
38 import java.util.concurrent.locks.ReentrantReadWriteLock;
39 import java.util.stream.Collectors;
40 import java.util.stream.Stream;
41
42 import static org.apache.archiva.indexer.ArchivaIndexManager.DEFAULT_INDEX_PATH;
43
44 /**
45  * Registry for repositories. This is the central entry point for repositories. It provides methods for
46  * retrieving, adding and removing repositories.
47  * <p>
48  * The modification methods addXX and removeXX persist the changes immediately to the configuration. If the
49  * configuration save fails the changes are rolled back.
50  * <p>
51  * TODO: Audit events
52  *
53  * @since 3.0
54  */
55 @Service("repositoryRegistry")
56 public class RepositoryRegistry implements ConfigurationListener, RepositoryEventHandler, RepositoryEventListener {
57
58     private static final Logger log = LoggerFactory.getLogger(RepositoryRegistry.class);
59
60     /**
61      * We inject all repository providers
62      */
63     @Inject
64     List<RepositoryProvider> repositoryProviders;
65
66     @Inject
67     IndexManagerFactory indexManagerFactory;
68
69     @Inject
70     ArchivaConfiguration archivaConfiguration;
71
72     @Inject
73     @Named("repositoryContentFactory#default")
74     RepositoryContentFactory repositoryContentFactory;
75
76     private List<RepositoryEventListener> listeners = new ArrayList<>();
77     private Map<EventType, List<RepositoryEventListener>> typeListenerMap = new HashMap<>();
78
79
80     private Map<String, ManagedRepository> managedRepositories = new HashMap<>();
81     private Map<String, ManagedRepository> uManagedRepository = Collections.unmodifiableMap(managedRepositories);
82
83     private Map<String, RemoteRepository> remoteRepositories = new HashMap<>();
84     private Map<String, RemoteRepository> uRemoteRepositories = Collections.unmodifiableMap(remoteRepositories);
85
86     private Map<String, RepositoryGroup> repositoryGroups = new HashMap<>();
87     private Map<String, RepositoryGroup> uRepositoryGroups = Collections.unmodifiableMap(repositoryGroups);
88
89     private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
90
91     private volatile boolean ignoreConfigEvents = false;
92
93     public void setArchivaConfiguration(ArchivaConfiguration archivaConfiguration) {
94         this.archivaConfiguration = archivaConfiguration;
95     }
96
97     @PostConstruct
98     private void initialize() {
99         rwLock.writeLock().lock();
100         try {
101             log.debug("Initializing repository registry");
102             updateManagedRepositoriesFromConfig();
103             updateRemoteRepositoriesFromConfig();
104
105             repositoryGroups.clear();
106             Map<String, RepositoryGroup> repositoryGroups = getRepositorGroupsFromConfig();
107             this.repositoryGroups.putAll(repositoryGroups);
108
109             // archivaConfiguration.addChangeListener(this);
110             archivaConfiguration.addListener(this);
111         } finally {
112             rwLock.writeLock().unlock();
113         }
114         pushEvent(new RepositoryRegistryEvent<>(RepositoryRegistryEvent.RegistryEventType.RELOADED, this));
115     }
116
117     @PreDestroy
118     public void destroy() {
119         for (ManagedRepository rep : managedRepositories.values()) {
120             rep.close();
121         }
122         managedRepositories.clear();
123         for (RemoteRepository repo : remoteRepositories.values()) {
124             repo.close();
125         }
126         remoteRepositories.clear();
127         pushEvent(new RepositoryRegistryEvent<>(RepositoryRegistryEvent.RegistryEventType.DESTROYED, this));
128     }
129
130
131     private Map<RepositoryType, RepositoryProvider> createProviderMap() {
132         Map<RepositoryType, RepositoryProvider> map = new HashMap<>();
133         if (repositoryProviders != null) {
134             for (RepositoryProvider provider : repositoryProviders) {
135                 for (RepositoryType type : provider.provides()) {
136                     map.put(type, provider);
137                 }
138             }
139         }
140         return map;
141     }
142
143     private RepositoryProvider getProvider(RepositoryType type) throws RepositoryException {
144         return repositoryProviders.stream().filter(repositoryProvider -> repositoryProvider.provides().contains(type)).findFirst().orElseThrow(() -> new RepositoryException("Repository type cannot be handled: " + type));
145     }
146
147     /*
148      * Updates the repositories
149      */
150     private void updateManagedRepositoriesFromConfig() {
151         try {
152
153             Set<String> configRepoIds = new HashSet<>();
154             List<ManagedRepositoryConfiguration> managedRepoConfigs =
155                     getArchivaConfiguration().getConfiguration().getManagedRepositories();
156
157             if (managedRepoConfigs == null) {
158                 return;
159             }
160
161             for (ManagedRepositoryConfiguration repoConfig : managedRepoConfigs) {
162                 ManagedRepository repo = putRepository(repoConfig, null);
163                 configRepoIds.add(repoConfig.getId());
164                 if (repo.supportsFeature(StagingRepositoryFeature.class)) {
165                     StagingRepositoryFeature stagF = repo.getFeature(StagingRepositoryFeature.class).get();
166                     if (stagF.getStagingRepository() != null) {
167                         configRepoIds.add(stagF.getStagingRepository().getId());
168                     }
169                 }
170             }
171             List<String> toRemove = managedRepositories.keySet().stream().filter(id -> !configRepoIds.contains(id)).collect(Collectors.toList());
172             for (String id : toRemove) {
173                 ManagedRepository removed = managedRepositories.remove(id);
174                 removed.close();
175             }
176         } catch (Throwable e) {
177             log.error("Could not initialize repositories from config: {}", e.getMessage(), e);
178             return;
179         }
180     }
181
182     private ManagedRepository createNewManagedRepository(RepositoryProvider provider, ManagedRepositoryConfiguration cfg) throws RepositoryException {
183         log.debug("Creating repo {}", cfg.getId());
184         ManagedRepository repo = provider.createManagedInstance(cfg);
185         repo.register(this);
186         updateRepositoryReferences(provider, repo, cfg, null);
187         return repo;
188
189     }
190
191     private String getStagingId(String repoId) {
192         return repoId + StagingRepositoryFeature.STAGING_REPO_POSTFIX;
193     }
194
195     @SuppressWarnings("unchecked")
196     private void updateRepositoryReferences(RepositoryProvider provider, ManagedRepository repo, ManagedRepositoryConfiguration cfg, Configuration configuration) throws RepositoryException {
197         log.debug("Updating references of repo {}", repo.getId());
198         if (repo.supportsFeature(StagingRepositoryFeature.class)) {
199             StagingRepositoryFeature feature = repo.getFeature(StagingRepositoryFeature.class).get();
200             if (feature.isStageRepoNeeded() && feature.getStagingRepository() == null) {
201                 ManagedRepository stageRepo = getManagedRepository(getStagingId(repo.getId()));
202                 if (stageRepo == null) {
203                     stageRepo = getStagingRepository(provider, cfg, configuration);
204                     managedRepositories.put(stageRepo.getId(), stageRepo);
205                     if (configuration != null) {
206                         replaceOrAddRepositoryConfig(provider.getManagedConfiguration(stageRepo), configuration);
207                     }
208                     pushEvent(new LifecycleEvent(LifecycleEvent.LifecycleEventType.REGISTERED, this, stageRepo));
209                 }
210                 feature.setStagingRepository(stageRepo);
211             }
212         }
213         if (repo instanceof EditableManagedRepository) {
214             EditableManagedRepository editableRepo = (EditableManagedRepository) repo;
215             if (repo.getContent() == null) {
216                 editableRepo.setContent(repositoryContentFactory.getManagedRepositoryContent(repo));
217                 editableRepo.getContent().setRepository(editableRepo);
218             }
219             log.debug("Index repo: " + repo.hasIndex());
220             if (repo.hasIndex() && ( repo.getIndexingContext() == null || !repo.getIndexingContext().isOpen() )) {
221                 log.debug("Creating indexing context for {}", repo.getId());
222                 createIndexingContext(editableRepo);
223             }
224         }
225         repo.register(this);
226     }
227
228     public ArchivaIndexManager getIndexManager(RepositoryType type) {
229         return indexManagerFactory.getIndexManager(type);
230     }
231
232     private void createIndexingContext(EditableRepository editableRepo) throws RepositoryException {
233         if (editableRepo.supportsFeature(IndexCreationFeature.class)) {
234             ArchivaIndexManager idxManager = getIndexManager(editableRepo.getType());
235             try {
236                 editableRepo.setIndexingContext(idxManager.createContext(editableRepo));
237                 idxManager.updateLocalIndexPath(editableRepo);
238             } catch (IndexCreationFailedException e) {
239                 throw new RepositoryException("Could not create index for repository " + editableRepo.getId() + ": " + e.getMessage(), e);
240             }
241         }
242     }
243
244     private ManagedRepository getStagingRepository(RepositoryProvider provider, ManagedRepositoryConfiguration baseRepoCfg, Configuration configuration) throws RepositoryException {
245         ManagedRepository stageRepo = getManagedRepository(getStagingId(baseRepoCfg.getId()));
246         if (stageRepo == null) {
247             stageRepo = provider.createStagingInstance(baseRepoCfg);
248             if (stageRepo.supportsFeature(StagingRepositoryFeature.class)) {
249                 stageRepo.getFeature(StagingRepositoryFeature.class).get().setStageRepoNeeded(false);
250             }
251             ManagedRepositoryConfiguration stageCfg = provider.getManagedConfiguration(stageRepo);
252             updateRepositoryReferences(provider, stageRepo, stageCfg, configuration);
253         }
254         return stageRepo;
255     }
256
257
258     private void updateRemoteRepositoriesFromConfig() {
259         try {
260             List<RemoteRepositoryConfiguration> remoteRepoConfigs =
261                     getArchivaConfiguration().getConfiguration().getRemoteRepositories();
262
263             if (remoteRepoConfigs == null) {
264                 return;
265             }
266             Set<String> repoIds = new HashSet<>();
267             for (RemoteRepositoryConfiguration repoConfig : remoteRepoConfigs) {
268                 putRepository(repoConfig, null);
269                 repoIds.add(repoConfig.getId());
270             }
271
272             List<String> toRemove = remoteRepositories.keySet().stream().filter(id -> !repoIds.contains(id)).collect(Collectors.toList());
273             for (String id : toRemove) {
274                 RemoteRepository removed = remoteRepositories.remove(id);
275                 removed.close();
276             }
277
278         } catch (Throwable e) {
279             log.error("Could not initialize remote repositories from config: {}", e.getMessage(), e);
280             return;
281         }
282     }
283
284     private RemoteRepository createNewRemoteRepository(RepositoryProvider provider, RemoteRepositoryConfiguration cfg) throws RepositoryException {
285         log.debug("Creating remote repo {}", cfg.getId());
286         RemoteRepository repo = provider.createRemoteInstance(cfg);
287         updateRepositoryReferences(provider, repo, cfg, null);
288         return repo;
289
290     }
291
292     private void updateRepositoryReferences(RepositoryProvider provider, RemoteRepository repo, RemoteRepositoryConfiguration cfg, Configuration configuration) throws RepositoryException {
293         if (repo instanceof EditableRemoteRepository && repo.getContent() == null) {
294             EditableRemoteRepository editableRepo = (EditableRemoteRepository) repo;
295             editableRepo.setContent(repositoryContentFactory.getRemoteRepositoryContent(repo));
296             if (repo.supportsFeature(IndexCreationFeature.class) && repo.getIndexingContext() == null) {
297                 createIndexingContext(editableRepo);
298             }
299         }
300         repo.register(this);
301     }
302
303     private Map<String, RepositoryGroup> getRepositorGroupsFromConfig() {
304         try {
305             List<RepositoryGroupConfiguration> repositoryGroupConfigurations =
306                     getArchivaConfiguration().getConfiguration().getRepositoryGroups();
307
308             if (repositoryGroupConfigurations == null) {
309                 return Collections.emptyMap();
310             }
311
312             Map<String, RepositoryGroup> repositoryGroupMap = new LinkedHashMap<>(repositoryGroupConfigurations.size());
313
314             Map<RepositoryType, RepositoryProvider> providerMap = createProviderMap();
315             for (RepositoryGroupConfiguration repoConfig : repositoryGroupConfigurations) {
316                 RepositoryType repositoryType = RepositoryType.valueOf(repoConfig.getType());
317                 if (providerMap.containsKey(repositoryType)) {
318                     try {
319                         RepositoryGroup repo = createNewRepositoryGroup(providerMap.get(repositoryType), repoConfig);
320                         repositoryGroupMap.put(repo.getId(), repo);
321                     } catch (Exception e) {
322                         log.error("Could not create repository group {}: {}", repoConfig.getId(), e.getMessage(), e);
323                     }
324                 }
325             }
326             return repositoryGroupMap;
327         } catch (Throwable e) {
328             log.error("Could not initialize repositories from config: {}", e.getMessage(), e);
329             return Collections.emptyMap();
330         }
331     }
332
333     private RepositoryGroup createNewRepositoryGroup(RepositoryProvider provider, RepositoryGroupConfiguration config) throws RepositoryException {
334         RepositoryGroup repositoryGroup = provider.createRepositoryGroup(config);
335         repositoryGroup.register(this);
336         updateRepositoryReferences(provider, repositoryGroup, config);
337         return repositoryGroup;
338     }
339
340     private void updateRepositoryReferences(RepositoryProvider provider, RepositoryGroup group, RepositoryGroupConfiguration configuration) {
341         if (group instanceof EditableRepositoryGroup) {
342             EditableRepositoryGroup eGroup = (EditableRepositoryGroup) group;
343             eGroup.setRepositories(configuration.getRepositories().stream().map(r -> getManagedRepository(r)).collect(Collectors.toList()));
344         }
345     }
346
347     private ArchivaConfiguration getArchivaConfiguration() {
348         return this.archivaConfiguration;
349     }
350
351     /**
352      * Returns all repositories that are registered. There is no defined order of the returned repositories.
353      *
354      * @return a list of managed and remote repositories
355      */
356     public Collection<Repository> getRepositories() {
357         rwLock.readLock().lock();
358         try {
359             return Stream.concat(managedRepositories.values().stream(), remoteRepositories.values().stream()).collect(Collectors.toList());
360         } finally {
361             rwLock.readLock().unlock();
362         }
363     }
364
365     /**
366      * Returns only the managed repositories. There is no defined order of the returned repositories.
367      *
368      * @return a list of managed repositories
369      */
370     public Collection<ManagedRepository> getManagedRepositories() {
371         rwLock.readLock().lock();
372         try {
373             return uManagedRepository.values();
374         } finally {
375             rwLock.readLock().unlock();
376         }
377     }
378
379     /**
380      * Returns only the remote repositories. There is no defined order of the returned repositories.
381      *
382      * @return a list of remote repositories
383      */
384     public Collection<RemoteRepository> getRemoteRepositories() {
385         rwLock.readLock().lock();
386         try {
387             return uRemoteRepositories.values();
388         } finally {
389             rwLock.readLock().unlock();
390         }
391     }
392
393     public Collection<RepositoryGroup> getRepositoryGroups() {
394         rwLock.readLock().lock();
395         try {
396             return uRepositoryGroups.values();
397         } finally {
398             rwLock.readLock().unlock();
399         }
400     }
401
402     /**
403      * Returns the repository with the given id. The returned repository may be a managed or remote repository.
404      * It returns null, if no repository is registered with the given id.
405      *
406      * @param repoId the repository id
407      * @return the repository if found, otherwise null
408      */
409     public Repository getRepository(String repoId) {
410         rwLock.readLock().lock();
411         try {
412             log.debug("getRepository {}", repoId);
413             if (managedRepositories.containsKey(repoId)) {
414                 log.debug("Managed repo");
415                 return managedRepositories.get(repoId);
416             } else if (remoteRepositories.containsKey(repoId)) {
417                 log.debug("Remote repo");
418                 return remoteRepositories.get(repoId);
419             } else if (repositoryGroups.containsKey(repoId)) {
420                 return repositoryGroups.get(repoId);
421             } else {
422                 return null;
423             }
424         } finally {
425             rwLock.readLock().unlock();
426         }
427     }
428
429     /**
430      * Convenience method, that returns the managed repository with the given id.
431      * It returns null, if no managed repository is registered with this id.
432      *
433      * @param repoId the repository id
434      * @return the managed repository if found, otherwise null
435      */
436     public ManagedRepository getManagedRepository(String repoId) {
437         rwLock.readLock().lock();
438         try {
439             return managedRepositories.get(repoId);
440         } finally {
441             rwLock.readLock().unlock();
442         }
443     }
444
445     /**
446      * Convenience method, that returns the remote repository with the given id.
447      * It returns null, if no remote repository is registered with this id.
448      *
449      * @param repoId the repository id
450      * @return the remote repository if found, otherwise null
451      */
452     public RemoteRepository getRemoteRepository(String repoId) {
453         rwLock.readLock().lock();
454         try {
455             return remoteRepositories.get(repoId);
456         } finally {
457             rwLock.readLock().unlock();
458         }
459     }
460
461     public RepositoryGroup getRepositoryGroup(String groupId) {
462         rwLock.readLock().lock();
463         try {
464             return repositoryGroups.get(groupId);
465         } finally {
466             rwLock.readLock().unlock();
467         }
468     }
469
470     /*
471      * The <code>ignoreConfigEvents</code> works only for synchronized configuration events.
472      * If the configuration throws async events, we cannot know, if the event is caused by this instance or another thread.
473      */
474     private void saveConfiguration(Configuration configuration) throws IndeterminateConfigurationException, RegistryException {
475         ignoreConfigEvents = true;
476         try {
477             getArchivaConfiguration().save(configuration);
478         } finally {
479             ignoreConfigEvents = false;
480         }
481     }
482
483     /**
484      * Adds a new repository to the current list, or replaces the repository definition with
485      * the same id, if it exists already.
486      * The change is saved to the configuration immediately.
487      *
488      * @param managedRepository the new repository.
489      * @throws RepositoryException if the new repository could not be saved to the configuration.
490      */
491     public ManagedRepository putRepository(ManagedRepository managedRepository) throws RepositoryException {
492         rwLock.writeLock().lock();
493         try {
494             final String id = managedRepository.getId();
495             if (remoteRepositories.containsKey(id)) {
496                 throw new RepositoryException("There exists a remote repository with id " + id + ". Could not update with managed repository.");
497             }
498             ManagedRepository originRepo = managedRepositories.put(id, managedRepository);
499             try {
500                 if (originRepo != null && originRepo != managedRepository) {
501                     originRepo.close();
502                 }
503                 RepositoryProvider provider = getProvider(managedRepository.getType());
504                 ManagedRepositoryConfiguration newCfg = provider.getManagedConfiguration(managedRepository);
505                 Configuration configuration = getArchivaConfiguration().getConfiguration();
506                 updateRepositoryReferences(provider, managedRepository, newCfg, configuration);
507                 ManagedRepositoryConfiguration oldCfg = configuration.findManagedRepositoryById(id);
508                 if (oldCfg != null) {
509                     configuration.removeManagedRepository(oldCfg);
510                 }
511                 configuration.addManagedRepository(newCfg);
512                 saveConfiguration(configuration);
513                 if (originRepo != managedRepository) {
514                     pushEvent(new LifecycleEvent<>(LifecycleEvent.LifecycleEventType.REGISTERED, this, managedRepository));
515                 } else {
516                     pushEvent(new LifecycleEvent<>(LifecycleEvent.LifecycleEventType.UPDATED, this, managedRepository));
517                 }
518                 return managedRepository;
519             } catch (Exception e) {
520                 // Rollback only partly, because repository is closed already
521                 if (originRepo != null) {
522                     managedRepositories.put(id, originRepo);
523                 } else {
524                     managedRepositories.remove(id);
525                 }
526                 log.error("Exception during configuration update {}", e.getMessage(), e);
527                 throw new RepositoryException("Could not save the configuration" + (e.getMessage() == null ? "" : ": " + e.getMessage()));
528             }
529         } finally {
530             rwLock.writeLock().unlock();
531         }
532     }
533
534     /**
535      * Adds a new repository or updates the repository with the same id, if it exists already.
536      * The configuration is saved immediately.
537      *
538      * @param managedRepositoryConfiguration the repository configuration
539      * @return the updated or created repository
540      * @throws RepositoryException if an error occurs, or the configuration is not valid.
541      */
542     public ManagedRepository putRepository(ManagedRepositoryConfiguration managedRepositoryConfiguration) throws RepositoryException {
543         rwLock.writeLock().lock();
544         try {
545             final String id = managedRepositoryConfiguration.getId();
546             final RepositoryType repositoryType = RepositoryType.valueOf(managedRepositoryConfiguration.getType());
547             Configuration configuration = getArchivaConfiguration().getConfiguration();
548             ManagedRepository repo = managedRepositories.get(id);
549             ManagedRepositoryConfiguration oldCfg = repo != null ? getProvider(repositoryType).getManagedConfiguration(repo) : null;
550             repo = putRepository(managedRepositoryConfiguration, configuration);
551             try {
552                 saveConfiguration(configuration);
553             } catch (IndeterminateConfigurationException | RegistryException e) {
554                 if (oldCfg != null) {
555                     getProvider(repositoryType).updateManagedInstance((EditableManagedRepository) repo, oldCfg);
556                 }
557                 log.error("Could not save the configuration for repository {}: {}", id, e.getMessage(), e);
558                 throw new RepositoryException("Could not save the configuration for repository " + id + ": " + e.getMessage());
559             }
560             return repo;
561         } finally {
562             rwLock.writeLock().unlock();
563         }
564
565     }
566
567     /**
568      * Adds a new repository or updates the repository with the same id. The given configuration object is updated, but
569      * the configuration is not saved.
570      *
571      * @param managedRepositoryConfiguration the new or changed managed repository configuration
572      * @param configuration                  the configuration object (may be <code>null</code>)
573      * @return the new or updated repository
574      * @throws RepositoryException if the configuration cannot be saved or updated
575      */
576     public ManagedRepository putRepository(ManagedRepositoryConfiguration managedRepositoryConfiguration, Configuration configuration) throws RepositoryException {
577         rwLock.writeLock().lock();
578         try {
579             final String id = managedRepositoryConfiguration.getId();
580             final RepositoryType repoType = RepositoryType.valueOf(managedRepositoryConfiguration.getType());
581             ManagedRepository repo;
582             boolean registeredNew = false;
583             repo = managedRepositories.get(id);
584             if (repo != null && repo.isOpen()) {
585                 if (repo instanceof EditableManagedRepository) {
586                     getProvider(repoType).updateManagedInstance((EditableManagedRepository) repo, managedRepositoryConfiguration);
587                 } else {
588                     throw new RepositoryException("The repository is not editable " + id);
589                 }
590             } else {
591                 repo = getProvider(repoType).createManagedInstance(managedRepositoryConfiguration);
592                 managedRepositories.put(id, repo);
593                 registeredNew = true;
594             }
595             updateRepositoryReferences(getProvider(repoType), repo, managedRepositoryConfiguration, configuration);
596             replaceOrAddRepositoryConfig(managedRepositoryConfiguration, configuration);
597             if (registeredNew) {
598                 pushEvent(new LifecycleEvent<>(LifecycleEvent.LifecycleEventType.REGISTERED, this, repo));
599             } else {
600                 pushEvent(new LifecycleEvent<>(LifecycleEvent.LifecycleEventType.UPDATED, this, repo));
601             }
602             return repo;
603         } finally {
604             rwLock.writeLock().unlock();
605         }
606     }
607
608
609     /**
610      * Adds a new repository group to the current list, or replaces the repository group definition with
611      * the same id, if it exists already.
612      * The change is saved to the configuration immediately.
613      *
614      * @param repositoryGroup the new repository group.
615      * @throws RepositoryException if the new repository group could not be saved to the configuration.
616      */
617     public RepositoryGroup putRepositoryGroup(RepositoryGroup repositoryGroup) throws RepositoryException {
618         rwLock.writeLock().lock();
619         try {
620             final String id = repositoryGroup.getId();
621             RepositoryGroup originRepoGroup = repositoryGroups.put(id, repositoryGroup);
622             try {
623                 if (originRepoGroup != null && originRepoGroup != repositoryGroup) {
624                     originRepoGroup.close();
625                 }
626                 RepositoryProvider provider = getProvider(repositoryGroup.getType());
627                 RepositoryGroupConfiguration newCfg = provider.getRepositoryGroupConfiguration(repositoryGroup);
628                 Configuration configuration = getArchivaConfiguration().getConfiguration();
629                 updateRepositoryReferences(provider, repositoryGroup, newCfg);
630                 RepositoryGroupConfiguration oldCfg = configuration.findRepositoryGroupById(id);
631                 if (oldCfg != null) {
632                     configuration.removeRepositoryGroup(oldCfg);
633                 }
634                 configuration.addRepositoryGroup(newCfg);
635                 saveConfiguration(configuration);
636                 return repositoryGroup;
637             } catch (Exception e) {
638                 // Rollback
639                 if (originRepoGroup != null) {
640                     repositoryGroups.put(id, originRepoGroup);
641                 } else {
642                     repositoryGroups.remove(id);
643                 }
644                 log.error("Exception during configuration update {}", e.getMessage(), e);
645                 throw new RepositoryException("Could not save the configuration" + (e.getMessage() == null ? "" : ": " + e.getMessage()));
646             }
647         } finally {
648             rwLock.writeLock().unlock();
649         }
650     }
651
652     /**
653      * Adds a new repository group or updates the repository with the same id, if it exists already.
654      * The configuration is saved immediately.
655      *
656      * @param repositoryGroupConfiguration the repository configuration
657      * @return the updated or created repository
658      * @throws RepositoryException if an error occurs, or the configuration is not valid.
659      */
660     public RepositoryGroup putRepositoryGroup(RepositoryGroupConfiguration repositoryGroupConfiguration) throws RepositoryException {
661         rwLock.writeLock().lock();
662         try {
663             final String id = repositoryGroupConfiguration.getId();
664             final RepositoryType repositoryType = RepositoryType.valueOf(repositoryGroupConfiguration.getType());
665             Configuration configuration = getArchivaConfiguration().getConfiguration();
666             RepositoryGroup repo = repositoryGroups.get(id);
667             RepositoryGroupConfiguration oldCfg = repo != null ? getProvider(repositoryType).getRepositoryGroupConfiguration(repo) : null;
668             repo = putRepositoryGroup(repositoryGroupConfiguration, configuration);
669             try {
670                 saveConfiguration(configuration);
671             } catch (IndeterminateConfigurationException | RegistryException e) {
672                 if (oldCfg != null) {
673                     getProvider(repositoryType).updateRepositoryGroupInstance((EditableRepositoryGroup) repo, oldCfg);
674                 }
675                 log.error("Could not save the configuration for repository group {}: {}", id, e.getMessage(), e);
676                 throw new RepositoryException("Could not save the configuration for repository group " + id + ": " + e.getMessage());
677             }
678             return repo;
679         } finally {
680             rwLock.writeLock().unlock();
681         }
682
683     }
684
685     /**
686      * Adds a new repository group or updates the repository group with the same id. The given configuration object is updated, but
687      * the configuration is not saved.
688      *
689      * @param repositoryGroupConfiguration The configuration of the new or changed repository group.
690      * @param configuration                The configuration object. If it is <code>null</code>, the configuration is not saved.
691      * @return The new or updated repository group
692      * @throws RepositoryException if the configuration cannot be saved or updated
693      */
694     public RepositoryGroup putRepositoryGroup(RepositoryGroupConfiguration repositoryGroupConfiguration, Configuration configuration) throws RepositoryException {
695         rwLock.writeLock().lock();
696         try {
697             final String id = repositoryGroupConfiguration.getId();
698             final RepositoryType repoType = RepositoryType.valueOf(repositoryGroupConfiguration.getType());
699             RepositoryGroup repo;
700             setRepositoryGroupDefaults(repositoryGroupConfiguration);
701             if (repositoryGroups.containsKey(id)) {
702                 repo = repositoryGroups.get(id);
703                 if (repo instanceof EditableRepositoryGroup) {
704                     getProvider(repoType).updateRepositoryGroupInstance((EditableRepositoryGroup) repo, repositoryGroupConfiguration);
705                 } else {
706                     throw new RepositoryException("The repository is not editable " + id);
707                 }
708             } else {
709                 repo = getProvider(repoType).createRepositoryGroup(repositoryGroupConfiguration);
710                 repositoryGroups.put(id, repo);
711             }
712             updateRepositoryReferences(getProvider(repoType), repo, repositoryGroupConfiguration);
713             replaceOrAddRepositoryConfig(repositoryGroupConfiguration, configuration);
714             return repo;
715         } finally {
716             rwLock.writeLock().unlock();
717         }
718     }
719
720     private void setRepositoryGroupDefaults(RepositoryGroupConfiguration repositoryGroupConfiguration) {
721         if (StringUtils.isEmpty(repositoryGroupConfiguration.getMergedIndexPath())) {
722             repositoryGroupConfiguration.setMergedIndexPath(DEFAULT_INDEX_PATH);
723         }
724         if (repositoryGroupConfiguration.getMergedIndexTtl() <= 0) {
725             repositoryGroupConfiguration.setMergedIndexTtl(300);
726         }
727         if (StringUtils.isEmpty(repositoryGroupConfiguration.getCronExpression())) {
728             repositoryGroupConfiguration.setCronExpression("0 0 03 ? * MON");
729         }
730     }
731
732     private void replaceOrAddRepositoryConfig(ManagedRepositoryConfiguration managedRepositoryConfiguration, Configuration configuration) {
733         if (configuration != null) {
734             ManagedRepositoryConfiguration oldCfg = configuration.findManagedRepositoryById(managedRepositoryConfiguration.getId());
735             if (oldCfg != null) {
736                 configuration.removeManagedRepository(oldCfg);
737             }
738             configuration.addManagedRepository(managedRepositoryConfiguration);
739         }
740     }
741
742     private void replaceOrAddRepositoryConfig(RemoteRepositoryConfiguration remoteRepositoryConfiguration, Configuration configuration) {
743         if (configuration != null) {
744             RemoteRepositoryConfiguration oldCfg = configuration.findRemoteRepositoryById(remoteRepositoryConfiguration.getId());
745             if (oldCfg != null) {
746                 configuration.removeRemoteRepository(oldCfg);
747             }
748             configuration.addRemoteRepository(remoteRepositoryConfiguration);
749         }
750     }
751
752     private void replaceOrAddRepositoryConfig(RepositoryGroupConfiguration repositoryGroupConfiguration, Configuration configuration) {
753         RepositoryGroupConfiguration oldCfg = configuration.findRepositoryGroupById(repositoryGroupConfiguration.getId());
754         if (oldCfg != null) {
755             configuration.removeRepositoryGroup(oldCfg);
756         }
757         configuration.addRepositoryGroup(repositoryGroupConfiguration);
758     }
759
760     public RemoteRepository putRepository(RemoteRepository remoteRepository, Configuration configuration) throws RepositoryException {
761         rwLock.writeLock().lock();
762         try {
763             final String id = remoteRepository.getId();
764             if (managedRepositories.containsKey(id)) {
765                 throw new RepositoryException("There exists a managed repository with id " + id + ". Could not update with remote repository.");
766             }
767             RemoteRepository originRepo = remoteRepositories.put(id, remoteRepository);
768             RemoteRepositoryConfiguration oldCfg = null;
769             RemoteRepositoryConfiguration newCfg;
770             try {
771                 if (originRepo != null && originRepo != remoteRepository) {
772                     originRepo.close();
773                 }
774                 final RepositoryProvider provider = getProvider(remoteRepository.getType());
775                 newCfg = provider.getRemoteConfiguration(remoteRepository);
776                 updateRepositoryReferences(provider, remoteRepository, newCfg, configuration);
777                 oldCfg = configuration.findRemoteRepositoryById(id);
778                 if (oldCfg != null) {
779                     configuration.removeRemoteRepository(oldCfg);
780                 }
781                 configuration.addRemoteRepository(newCfg);
782                 if (remoteRepository != originRepo) {
783                     pushEvent(new LifecycleEvent<>(LifecycleEvent.LifecycleEventType.REGISTERED, this, remoteRepository));
784                 } else {
785                     pushEvent(new LifecycleEvent<>(LifecycleEvent.LifecycleEventType.UPDATED, this, remoteRepository));
786                 }
787                 return remoteRepository;
788             } catch (Exception e) {
789                 // Rollback
790                 if (originRepo != null) {
791                     remoteRepositories.put(id, originRepo);
792                 } else {
793                     remoteRepositories.remove(id);
794                 }
795                 if (oldCfg != null) {
796                     RemoteRepositoryConfiguration cfg = configuration.findRemoteRepositoryById(id);
797                     if (cfg != null) {
798                         configuration.removeRemoteRepository(cfg);
799                         configuration.addRemoteRepository(oldCfg);
800                     }
801                 }
802                 log.error("Error while adding remote repository {}", e.getMessage(), e);
803                 throw new RepositoryException("Could not save the configuration" + (e.getMessage() == null ? "" : ": " + e.getMessage()));
804             }
805         } finally {
806             rwLock.writeLock().unlock();
807         }
808     }
809
810     /**
811      * Adds a remote repository, or overwrites the repository definition with the same id, if it exists already.
812      * The modification is saved to the configuration immediately.
813      *
814      * @param remoteRepository the remote repository to add
815      * @throws RepositoryException if an error occurs during configuration save
816      */
817     public RemoteRepository putRepository(RemoteRepository remoteRepository) throws RepositoryException {
818         rwLock.writeLock().lock();
819         try {
820             Configuration configuration = getArchivaConfiguration().getConfiguration();
821             try {
822                 RemoteRepository repo = putRepository(remoteRepository, configuration);
823                 saveConfiguration(configuration);
824                 return repo;
825             } catch (RegistryException | IndeterminateConfigurationException e) {
826                 log.error("Error while saving remote repository {}", e.getMessage(), e);
827                 throw new RepositoryException("Could not save the configuration" + (e.getMessage() == null ? "" : ": " + e.getMessage()));
828             }
829         } finally {
830             rwLock.writeLock().unlock();
831         }
832     }
833
834     /**
835      * Adds a new repository or updates the repository with the same id, if it exists already.
836      * The configuration is saved immediately.
837      *
838      * @param remoteRepositoryConfiguration the repository configuration
839      * @return the updated or created repository
840      * @throws RepositoryException if an error occurs, or the configuration is not valid.
841      */
842     public RemoteRepository putRepository(RemoteRepositoryConfiguration remoteRepositoryConfiguration) throws RepositoryException {
843         rwLock.writeLock().lock();
844         try {
845             final String id = remoteRepositoryConfiguration.getId();
846             final RepositoryType repositoryType = RepositoryType.valueOf(remoteRepositoryConfiguration.getType());
847             Configuration configuration = getArchivaConfiguration().getConfiguration();
848             RemoteRepository repo = remoteRepositories.get(id);
849             RemoteRepositoryConfiguration oldCfg = repo != null ? getProvider(repositoryType).getRemoteConfiguration(repo) : null;
850             repo = putRepository(remoteRepositoryConfiguration, configuration);
851             try {
852                 saveConfiguration(configuration);
853             } catch (IndeterminateConfigurationException | RegistryException e) {
854                 if (oldCfg != null) {
855                     getProvider(repositoryType).updateRemoteInstance((EditableRemoteRepository) repo, oldCfg);
856                 }
857                 log.error("Could not save the configuration for repository {}: {}", id, e.getMessage(), e);
858                 throw new RepositoryException("Could not save the configuration for repository " + id + ": " + e.getMessage());
859             }
860             return repo;
861         } finally {
862             rwLock.writeLock().unlock();
863         }
864
865     }
866
867     /**
868      * Adds a new repository or updates the repository with the same id. The given configuration object is updated, but
869      * the configuration is not saved.
870      *
871      * @param remoteRepositoryConfiguration the new or changed repository configuration
872      * @param configuration                 the configuration object
873      * @return the new or updated repository
874      * @throws RepositoryException if the configuration cannot be saved or updated
875      */
876     @SuppressWarnings("unchecked")
877     public RemoteRepository putRepository(RemoteRepositoryConfiguration remoteRepositoryConfiguration, Configuration configuration) throws RepositoryException {
878         rwLock.writeLock().lock();
879         try {
880             final String id = remoteRepositoryConfiguration.getId();
881             final RepositoryType repoType = RepositoryType.valueOf(remoteRepositoryConfiguration.getType());
882             RemoteRepository repo;
883             boolean registeredNew = false;
884             repo = remoteRepositories.get(id);
885             if (repo != null && repo.isOpen()) {
886                 if (repo instanceof EditableRemoteRepository) {
887                     getProvider(repoType).updateRemoteInstance((EditableRemoteRepository) repo, remoteRepositoryConfiguration);
888                 } else {
889                     throw new RepositoryException("The repository is not editable " + id);
890                 }
891             } else {
892                 repo = getProvider(repoType).createRemoteInstance(remoteRepositoryConfiguration);
893                 remoteRepositories.put(id, repo);
894                 registeredNew = true;
895             }
896             updateRepositoryReferences(getProvider(repoType), repo, remoteRepositoryConfiguration, configuration);
897             replaceOrAddRepositoryConfig(remoteRepositoryConfiguration, configuration);
898             if (registeredNew) {
899                 pushEvent(new LifecycleEvent(LifecycleEvent.LifecycleEventType.REGISTERED, this, repo));
900             } else {
901                 pushEvent(new LifecycleEvent(LifecycleEvent.LifecycleEventType.UPDATED, this, repo));
902             }
903             return repo;
904         } finally {
905             rwLock.writeLock().unlock();
906         }
907
908
909     }
910
911     public void removeRepository(String repoId) throws RepositoryException {
912         Repository repo = getRepository(repoId);
913         if (repo != null) {
914             removeRepository(repo);
915         }
916     }
917
918     public void removeRepository(Repository repo) throws RepositoryException {
919         if (repo == null) {
920             log.warn("Trying to remove null repository");
921             return;
922         }
923         if (repo instanceof RemoteRepository) {
924             removeRepository((RemoteRepository) repo);
925         } else if (repo instanceof ManagedRepository) {
926             removeRepository((ManagedRepository) repo);
927         } else if (repo instanceof RepositoryGroup) {
928             removeRepositoryGroup((RepositoryGroup) repo);
929         } else {
930             throw new RepositoryException("Repository type not known: " + repo.getClass());
931         }
932     }
933
934     /**
935      * Removes a managed repository from the registry and configuration, if it exists.
936      * The change is saved to the configuration immediately.
937      *
938      * @param managedRepository the managed repository to remove
939      * @throws RepositoryException if a error occurs during configuration save
940      */
941     public void removeRepository(ManagedRepository managedRepository) throws RepositoryException {
942         if (managedRepository == null) {
943             return;
944         }
945         final String id = managedRepository.getId();
946         ManagedRepository repo = getManagedRepository(id);
947         if (repo != null) {
948             rwLock.writeLock().lock();
949             try {
950                 repo = managedRepositories.remove(id);
951                 if (repo != null) {
952                     repo.close();
953                     removeRepositoryFromGroups(repo);
954                     Configuration configuration = getArchivaConfiguration().getConfiguration();
955                     ManagedRepositoryConfiguration cfg = configuration.findManagedRepositoryById(id);
956                     if (cfg != null) {
957                         configuration.removeManagedRepository(cfg);
958                     }
959                     saveConfiguration(configuration);
960                 }
961                 pushEvent(new LifecycleEvent<>(LifecycleEvent.LifecycleEventType.UNREGISTERED, this, repo));
962             } catch (RegistryException | IndeterminateConfigurationException e) {
963                 // Rollback
964                 log.error("Could not save config after repository removal: {}", e.getMessage(), e);
965                 managedRepositories.put(repo.getId(), repo);
966                 throw new RepositoryException("Could not save configuration after repository removal: " + e.getMessage());
967             } finally {
968                 rwLock.writeLock().unlock();
969             }
970         }
971     }
972
973     private void removeRepositoryFromGroups(ManagedRepository repo) {
974         if (repo != null) {
975             repositoryGroups.values().stream().filter(repoGroup -> repoGroup instanceof EditableRepository).
976                     map(repoGroup -> (EditableRepositoryGroup) repoGroup).forEach(repoGroup -> repoGroup.removeRepository(repo));
977         }
978     }
979
980     public void removeRepository(ManagedRepository managedRepository, Configuration configuration) throws RepositoryException {
981         if (managedRepository == null) {
982             return;
983         }
984         final String id = managedRepository.getId();
985         ManagedRepository repo = getManagedRepository(id);
986         if (repo != null) {
987             rwLock.writeLock().lock();
988             try {
989                 repo = managedRepositories.remove(id);
990                 if (repo != null) {
991                     repo.close();
992                     removeRepositoryFromGroups(repo);
993                     ManagedRepositoryConfiguration cfg = configuration.findManagedRepositoryById(id);
994                     if (cfg != null) {
995                         configuration.removeManagedRepository(cfg);
996                     }
997                 }
998                 pushEvent(new LifecycleEvent<>(LifecycleEvent.LifecycleEventType.UNREGISTERED, this, repo));
999             } finally {
1000                 rwLock.writeLock().unlock();
1001             }
1002         }
1003
1004     }
1005
1006
1007     /**
1008      * Removes a repository group from the registry and configuration, if it exists.
1009      * The change is saved to the configuration immediately.
1010      *
1011      * @param repositoryGroup the repository group to remove
1012      * @throws RepositoryException if a error occurs during configuration save
1013      */
1014     public void removeRepositoryGroup(RepositoryGroup repositoryGroup) throws RepositoryException {
1015         if (repositoryGroup == null) {
1016             return;
1017         }
1018         final String id = repositoryGroup.getId();
1019         RepositoryGroup repo = getRepositoryGroup(id);
1020         if (repo != null) {
1021             rwLock.writeLock().lock();
1022             try {
1023                 repo = repositoryGroups.remove(id);
1024                 if (repo != null) {
1025                     repo.close();
1026                     Configuration configuration = getArchivaConfiguration().getConfiguration();
1027                     RepositoryGroupConfiguration cfg = configuration.findRepositoryGroupById(id);
1028                     if (cfg != null) {
1029                         configuration.removeRepositoryGroup(cfg);
1030                     }
1031                     saveConfiguration(configuration);
1032                 }
1033
1034             } catch (RegistryException | IndeterminateConfigurationException e) {
1035                 // Rollback
1036                 log.error("Could not save config after repository removal: {}", e.getMessage(), e);
1037                 repositoryGroups.put(repo.getId(), repo);
1038                 throw new RepositoryException("Could not save configuration after repository removal: " + e.getMessage());
1039             } finally {
1040                 rwLock.writeLock().unlock();
1041             }
1042         }
1043     }
1044
1045     public void removeRepositoryGroup(RepositoryGroup repositoryGroup, Configuration configuration) throws RepositoryException {
1046         if (repositoryGroup == null) {
1047             return;
1048         }
1049         final String id = repositoryGroup.getId();
1050         RepositoryGroup repo = getRepositoryGroup(id);
1051         if (repo != null) {
1052             rwLock.writeLock().lock();
1053             try {
1054                 repo = repositoryGroups.remove(id);
1055                 if (repo != null) {
1056                     repo.close();
1057                     RepositoryGroupConfiguration cfg = configuration.findRepositoryGroupById(id);
1058                     if (cfg != null) {
1059                         configuration.removeRepositoryGroup(cfg);
1060                     }
1061                 }
1062             } finally {
1063                 rwLock.writeLock().unlock();
1064             }
1065         }
1066
1067     }
1068
1069     private void doRemoveRepo(RemoteRepository repo, Configuration configuration) {
1070         repo.close();
1071         RemoteRepositoryConfiguration cfg = configuration.findRemoteRepositoryById(repo.getId());
1072         if (cfg != null) {
1073             configuration.removeRemoteRepository(cfg);
1074         }
1075         List<ProxyConnectorConfiguration> proxyConnectors = new ArrayList<>(configuration.getProxyConnectors());
1076         for (ProxyConnectorConfiguration proxyConnector : proxyConnectors) {
1077             if (StringUtils.equals(proxyConnector.getTargetRepoId(), repo.getId())) {
1078                 configuration.removeProxyConnector(proxyConnector);
1079             }
1080         }
1081     }
1082
1083     /**
1084      * Removes the remote repository from the registry and configuration.
1085      * The change is saved to the configuration immediately.
1086      *
1087      * @param remoteRepository the remote repository to remove
1088      * @throws RepositoryException if a error occurs during configuration save
1089      */
1090     public void removeRepository(RemoteRepository remoteRepository) throws RepositoryException {
1091         if (remoteRepository == null) {
1092             return;
1093         }
1094         final String id = remoteRepository.getId();
1095         RemoteRepository repo = getRemoteRepository(id);
1096         if (repo != null) {
1097             rwLock.writeLock().lock();
1098             try {
1099                 repo = remoteRepositories.remove(id);
1100                 if (repo != null) {
1101                     Configuration configuration = getArchivaConfiguration().getConfiguration();
1102                     doRemoveRepo(repo, configuration);
1103                     saveConfiguration(configuration);
1104                 }
1105                 pushEvent(new LifecycleEvent<>(LifecycleEvent.LifecycleEventType.UNREGISTERED, this, repo));
1106             } catch (RegistryException | IndeterminateConfigurationException e) {
1107                 // Rollback
1108                 log.error("Could not save config after repository removal: {}", e.getMessage(), e);
1109                 remoteRepositories.put(repo.getId(), repo);
1110                 throw new RepositoryException("Could not save configuration after repository removal: " + e.getMessage());
1111             } finally {
1112                 rwLock.writeLock().unlock();
1113             }
1114         }
1115     }
1116
1117     public void removeRepository(RemoteRepository remoteRepository, Configuration configuration) throws RepositoryException {
1118         if (remoteRepository == null) {
1119             return;
1120         }
1121         final String id = remoteRepository.getId();
1122         RemoteRepository repo = getRemoteRepository(id);
1123         if (repo != null) {
1124             rwLock.writeLock().lock();
1125             try {
1126                 repo = remoteRepositories.remove(id);
1127                 if (repo != null) {
1128                     doRemoveRepo(repo, configuration);
1129                 }
1130                 pushEvent(new LifecycleEvent<>(LifecycleEvent.LifecycleEventType.UNREGISTERED, this, repo));
1131             } finally {
1132                 rwLock.writeLock().unlock();
1133             }
1134         }
1135
1136     }
1137
1138     /**
1139      * Reloads the registry from the configuration.
1140      */
1141     public void reload() {
1142         initialize();
1143     }
1144
1145     /**
1146      * Resets the indexing context of a given repository.
1147      *
1148      * @param repository The repository
1149      * @throws IndexUpdateFailedException If the index could not be resetted.
1150      */
1151     public void resetIndexingContext(Repository repository) throws IndexUpdateFailedException {
1152         if (repository.hasIndex() && repository instanceof EditableRepository) {
1153             EditableRepository eRepo = (EditableRepository) repository;
1154             ArchivaIndexingContext newCtx = getIndexManager(repository.getType()).reset(repository.getIndexingContext());
1155             eRepo.setIndexingContext(newCtx);
1156         }
1157     }
1158
1159
1160     /**
1161      * Creates a new repository instance with the same settings as this one. The cloned repository is not
1162      * registered or saved to the configuration.
1163      *
1164      * @param repo The origin repository
1165      * @return The cloned repository.
1166      */
1167     public ManagedRepository clone(ManagedRepository repo, String newId) throws RepositoryException {
1168         if (managedRepositories.containsKey(newId) || remoteRepositories.containsKey(newId)) {
1169             throw new RepositoryException("The given id exists already " + newId);
1170         }
1171         RepositoryProvider provider = getProvider(repo.getType());
1172         ManagedRepositoryConfiguration cfg = provider.getManagedConfiguration(repo);
1173         cfg.setId(newId);
1174         ManagedRepository cloned = provider.createManagedInstance(cfg);
1175         cloned.register(this);
1176         return cloned;
1177     }
1178
1179     public <T extends Repository> Repository clone(T repo, String newId) throws RepositoryException {
1180         if (repo instanceof RemoteRepository) {
1181             return this.clone((RemoteRepository) repo, newId);
1182         } else if (repo instanceof ManagedRepository) {
1183             return this.clone((ManagedRepository) repo, newId);
1184         } else {
1185             throw new RepositoryException("This repository class is not supported " + repo.getClass().getName());
1186         }
1187     }
1188
1189     /**
1190      * Creates a new repository instance with the same settings as this one. The cloned repository is not
1191      * registered or saved to the configuration.
1192      *
1193      * @param repo The origin repository
1194      * @return The cloned repository.
1195      */
1196     public RemoteRepository clone(RemoteRepository repo, String newId) throws RepositoryException {
1197         if (managedRepositories.containsKey(newId) || remoteRepositories.containsKey(newId)) {
1198             throw new RepositoryException("The given id exists already " + newId);
1199         }
1200         RepositoryProvider provider = getProvider(repo.getType());
1201         RemoteRepositoryConfiguration cfg = provider.getRemoteConfiguration(repo);
1202         cfg.setId(newId);
1203         RemoteRepository cloned = provider.createRemoteInstance(cfg);
1204         cloned.register(this);
1205         return cloned;
1206     }
1207
1208
1209     @Override
1210     public void configurationEvent(ConfigurationEvent event) {
1211         // Note: the ignoreConfigEvents flag does not work, if the config events are asynchronous.
1212         if (!ignoreConfigEvents) {
1213             reload();
1214         }
1215     }
1216
1217
1218     @Override
1219     public void register(RepositoryEventListener listener) {
1220         if (!this.listeners.contains(listener)) {
1221             this.listeners.add(listener);
1222         }
1223     }
1224
1225     @Override
1226     public void register(RepositoryEventListener listener, EventType type) {
1227         List<RepositoryEventListener> listeners;
1228         if (typeListenerMap.containsKey(type)) {
1229             listeners = typeListenerMap.get(type);
1230         } else {
1231             listeners = new ArrayList<>();
1232             typeListenerMap.put(type, listeners);
1233         }
1234         if (!listeners.contains(listener)) {
1235             listeners.add(listener);
1236         }
1237     }
1238
1239     @Override
1240     public void register(RepositoryEventListener listener, Set<? extends EventType> types) {
1241         for (EventType type : types) {
1242             register(listener, type);
1243         }
1244     }
1245
1246     @Override
1247     public void unregister(RepositoryEventListener listener) {
1248         this.listeners.remove(listener);
1249         for (List<RepositoryEventListener> listeners : typeListenerMap.values()) {
1250             listeners.remove(listener);
1251         }
1252     }
1253
1254     @Override
1255     public void clearListeners() {
1256         this.listeners.clear();
1257         this.typeListenerMap.clear();
1258     }
1259
1260     @SuppressWarnings("unchecked")
1261     @Override
1262     public void raise(Event event) {
1263         // To avoid event cycles:
1264         if (sameOriginator(event)) {
1265             return;
1266         }
1267         if (event instanceof IndexCreationEvent) {
1268             handleIndexCreationEvent((IndexCreationEvent) event);
1269         }
1270         // We propagate all events to our listeners, but with context of repository registry
1271         pushEvent(event.recreate(this));
1272     }
1273
1274     private void handleIndexCreationEvent(IndexCreationEvent event) {
1275         IndexCreationEvent idxEvent = event;
1276         if (managedRepositories.containsKey(idxEvent.getRepository().getId()) ||
1277                 remoteRepositories.containsKey(idxEvent.getRepository().getId())) {
1278             EditableRepository repo = (EditableRepository) idxEvent.getRepository();
1279             if (repo != null && repo.getIndexingContext() != null) {
1280                 try {
1281                     ArchivaIndexManager idxmgr = getIndexManager(repo.getType());
1282                     if (idxmgr != null) {
1283                         ArchivaIndexingContext newCtx = idxmgr.move(repo.getIndexingContext(), repo);
1284                         repo.setIndexingContext(newCtx);
1285                         idxmgr.updateLocalIndexPath(repo);
1286                     }
1287
1288                 } catch (IndexCreationFailedException e) {
1289                     log.error("Could not move index to new directory {}", e.getMessage(), e);
1290                 }
1291             }
1292         }
1293     }
1294
1295     private boolean sameOriginator(Event event) {
1296         if (event.getOriginator() == this) {
1297             return true;
1298         } else if (event.hasPreviousEvent()) {
1299             return sameOriginator(event.getPreviousEvent());
1300         } else {
1301             return false;
1302         }
1303     }
1304
1305     private void pushEvent(Event<RepositoryRegistry> event) {
1306         callListeners(event, listeners);
1307         if (typeListenerMap.containsKey(event.getType())) {
1308             callListeners(event, typeListenerMap.get(event.getType()));
1309         }
1310     }
1311
1312     private void callListeners(final Event<RepositoryRegistry> event, final List<RepositoryEventListener> evtListeners) {
1313         for (RepositoryEventListener listener : evtListeners) {
1314             try {
1315                 listener.raise(event);
1316             } catch (Throwable e) {
1317                 log.error("Could not raise event {} on listener {}: {}", event, listener, e.getMessage());
1318             }
1319         }
1320     }
1321
1322
1323 }