]> source.dussan.org Git - archiva.git/blob
d3eb836c2ef8c3f50e6a33ba4d3ca95d96ab81ea
[archiva.git] /
1 package org.apache.archiva.repository.base;
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.components.registry.RegistryException;
23 import org.apache.archiva.configuration.ArchivaConfiguration;
24 import org.apache.archiva.configuration.Configuration;
25 import org.apache.archiva.configuration.ConfigurationEvent;
26 import org.apache.archiva.configuration.ConfigurationListener;
27 import org.apache.archiva.configuration.IndeterminateConfigurationException;
28 import org.apache.archiva.configuration.ManagedRepositoryConfiguration;
29 import org.apache.archiva.configuration.ProxyConnectorConfiguration;
30 import org.apache.archiva.configuration.RemoteRepositoryConfiguration;
31 import org.apache.archiva.configuration.RepositoryGroupConfiguration;
32 import org.apache.archiva.event.Event;
33 import org.apache.archiva.event.EventHandler;
34 import org.apache.archiva.event.EventManager;
35 import org.apache.archiva.event.EventType;
36 import org.apache.archiva.indexer.ArchivaIndexManager;
37 import org.apache.archiva.indexer.ArchivaIndexingContext;
38 import org.apache.archiva.indexer.IndexCreationFailedException;
39 import org.apache.archiva.indexer.IndexManagerFactory;
40 import org.apache.archiva.indexer.IndexUpdateFailedException;
41 import org.apache.archiva.repository.CheckedResult;
42 import org.apache.archiva.repository.EditableManagedRepository;
43 import org.apache.archiva.repository.EditableRemoteRepository;
44 import org.apache.archiva.repository.EditableRepository;
45 import org.apache.archiva.repository.ManagedRepository;
46 import org.apache.archiva.repository.RemoteRepository;
47 import org.apache.archiva.repository.Repository;
48 import org.apache.archiva.repository.RepositoryContentFactory;
49 import org.apache.archiva.repository.RepositoryException;
50 import org.apache.archiva.repository.RepositoryGroup;
51 import org.apache.archiva.repository.RepositoryProvider;
52 import org.apache.archiva.repository.RepositoryRegistry;
53 import org.apache.archiva.repository.RepositoryType;
54 import org.apache.archiva.repository.UnsupportedRepositoryTypeException;
55 import org.apache.archiva.repository.base.validation.CommonGroupValidator;
56 import org.apache.archiva.repository.event.LifecycleEvent;
57 import org.apache.archiva.repository.event.RepositoryEvent;
58 import org.apache.archiva.repository.event.RepositoryIndexEvent;
59 import org.apache.archiva.repository.event.RepositoryRegistryEvent;
60 import org.apache.archiva.repository.features.IndexCreationFeature;
61 import org.apache.archiva.repository.features.StagingRepositoryFeature;
62 import org.apache.archiva.repository.metadata.MetadataReader;
63 import org.apache.archiva.repository.storage.StorageAsset;
64 import org.apache.archiva.repository.validation.RepositoryChecker;
65 import org.apache.archiva.repository.validation.RepositoryValidator;
66 import org.apache.archiva.repository.validation.ValidationError;
67 import org.apache.archiva.repository.validation.ValidationResponse;
68 import org.apache.commons.collections4.ListUtils;
69 import org.apache.commons.lang3.StringUtils;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
72 import org.springframework.stereotype.Service;
73
74 import javax.annotation.PostConstruct;
75 import javax.annotation.PreDestroy;
76 import javax.inject.Inject;
77 import javax.inject.Named;
78 import java.util.ArrayList;
79 import java.util.Collection;
80 import java.util.Collections;
81 import java.util.HashMap;
82 import java.util.HashSet;
83 import java.util.List;
84 import java.util.Map;
85 import java.util.Set;
86 import java.util.TreeSet;
87 import java.util.concurrent.atomic.AtomicBoolean;
88 import java.util.concurrent.locks.ReentrantReadWriteLock;
89 import java.util.stream.Collectors;
90 import java.util.stream.Stream;
91
92 /**
93  * Registry for repositories. This is the central entry point for repositories. It provides methods for
94  * retrieving, adding and removing repositories.
95  * <p>
96  * The modification methods addXX and removeXX persist the changes immediately to the configuration. If the
97  * configuration save fails the changes are rolled back.
98  * <p>
99  * TODO: Audit events
100  *
101  * @since 3.0
102  */
103 @Service( "repositoryRegistry" )
104 public class ArchivaRepositoryRegistry implements ConfigurationListener, EventHandler<Event>,
105     RepositoryRegistry
106 {
107
108     private static final Logger log = LoggerFactory.getLogger( RepositoryRegistry.class );
109
110     /**
111      * We inject all repository providers
112      */
113     @Inject
114     List<RepositoryProvider> repositoryProviders;
115
116     @Inject
117     IndexManagerFactory indexManagerFactory;
118
119     @Inject
120     List<MetadataReader> metadataReaderList;
121
122     @Inject
123     @Named( "repositoryContentFactory#default" )
124     RepositoryContentFactory repositoryContentFactory;
125
126
127     private final EventManager eventManager;
128
129
130     private Map<String, ManagedRepository> managedRepositories = new HashMap<>( );
131     private Map<String, ManagedRepository> uManagedRepository = Collections.unmodifiableMap( managedRepositories );
132
133     private Map<String, RemoteRepository> remoteRepositories = new HashMap<>( );
134     private Map<String, RemoteRepository> uRemoteRepositories = Collections.unmodifiableMap( remoteRepositories );
135
136     private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock( );
137
138     private RepositoryGroupHandler groupHandler;
139     private final Set<RepositoryValidator<? extends Repository>> validators;
140     private final ConfigurationHandler configurationHandler;
141
142
143     private AtomicBoolean groups_initalized = new AtomicBoolean( false );
144     private AtomicBoolean managed_initialized = new AtomicBoolean( false );
145     private AtomicBoolean remote_initialized = new AtomicBoolean( false );
146
147
148     public ArchivaRepositoryRegistry( ConfigurationHandler configurationHandler, List<RepositoryValidator<? extends Repository>> validatorList )
149     {
150         this.eventManager = new EventManager( this );
151         this.configurationHandler = configurationHandler;
152         this.validators = initValidatorList( validatorList );
153     }
154
155
156     private Set<RepositoryValidator<? extends Repository>> initValidatorList( List<RepositoryValidator<? extends Repository>> validators )
157     {
158         TreeSet<RepositoryValidator<? extends Repository>> val = new TreeSet<>( );
159         for (RepositoryValidator<? extends Repository> validator : validators) {
160             val.add( validator );
161         }
162         return val;
163     }
164
165     @Override
166     public void setArchivaConfiguration( ArchivaConfiguration archivaConfiguration )
167     {
168         this.configurationHandler.setArchivaConfiguration( archivaConfiguration );
169     }
170
171     @PostConstruct
172     private void initialize( )
173     {
174         rwLock.writeLock( ).lock( );
175         try
176         {
177             log.debug( "Initializing repository registry" );
178             updateManagedRepositoriesFromConfig( );
179             pushEvent( new RepositoryRegistryEvent( RepositoryRegistryEvent.MANAGED_REPOS_INITIALIZED, this ) );
180             managed_initialized.set( true );
181             updateRemoteRepositoriesFromConfig( );
182             pushEvent( new RepositoryRegistryEvent( RepositoryRegistryEvent.REMOTE_REPOS_INITIALIZED, this ) );
183             remote_initialized.set( true );
184
185             initializeRepositoryGroups( );
186
187             for ( RepositoryProvider provider : repositoryProviders )
188             {
189                 provider.addRepositoryEventHandler( this );
190             }
191             this.configurationHandler.addListener( this );
192         }
193         finally
194         {
195             rwLock.writeLock( ).unlock( );
196         }
197         pushEvent( new RepositoryRegistryEvent( RepositoryRegistryEvent.RELOADED, this ) );
198         if ( managed_initialized.get( ) && remote_initialized.get( ) && groups_initalized.get( ) )
199         {
200             pushEvent( new RepositoryRegistryEvent( RepositoryRegistryEvent.INITIALIZED, this ) );
201         }
202     }
203
204     private void initializeRepositoryGroups( )
205     {
206         if ( this.groupHandler != null )
207         {
208             this.groupHandler.initializeFromConfig( );
209             this.groups_initalized.set( true );
210             pushEvent( new RepositoryRegistryEvent( RepositoryRegistryEvent.GROUPS_INITIALIZED, this ) );
211         }
212     }
213
214     public void registerGroupHandler( RepositoryGroupHandler groupHandler )
215     {
216         this.groupHandler = groupHandler;
217         initializeRepositoryGroups( );
218         if ( managed_initialized.get( ) && remote_initialized.get( ) && groups_initalized.get( ) )
219         {
220             pushEvent( new RepositoryRegistryEvent( RepositoryRegistryEvent.INITIALIZED, this ) );
221         }
222     }
223
224
225     @PreDestroy
226     public void destroy( )
227     {
228         for ( ManagedRepository rep : managedRepositories.values( ) )
229         {
230             rep.close( );
231         }
232         managedRepositories.clear( );
233         for ( RemoteRepository repo : remoteRepositories.values( ) )
234         {
235             repo.close( );
236         }
237         remoteRepositories.clear( );
238         groupHandler.close( );
239         pushEvent( new RepositoryRegistryEvent( RepositoryRegistryEvent.DESTROYED, this ) );
240     }
241
242
243     protected Map<RepositoryType, RepositoryProvider> getRepositoryProviderMap( )
244     {
245         Map<RepositoryType, RepositoryProvider> map = new HashMap<>( );
246         if ( repositoryProviders != null )
247         {
248             for ( RepositoryProvider provider : repositoryProviders )
249             {
250                 for ( RepositoryType type : provider.provides( ) )
251                 {
252                     map.put( type, provider );
253                 }
254             }
255         }
256         return map;
257     }
258
259     protected RepositoryProvider getProvider( RepositoryType type ) throws RepositoryException
260     {
261         return repositoryProviders.stream( ).filter( repositoryProvider -> repositoryProvider.provides( ).contains( type ) ).findFirst( ).orElseThrow( ( ) -> new RepositoryException( "Repository type cannot be handled: " + type ) );
262     }
263
264     /*
265      * Updates the repositories
266      */
267     private void updateManagedRepositoriesFromConfig( )
268     {
269         try
270         {
271
272             Set<String> configRepoIds = new HashSet<>( );
273             List<ManagedRepositoryConfiguration> managedRepoConfigs =
274                 configurationHandler.getBaseConfiguration( ).getManagedRepositories( );
275
276             if ( managedRepoConfigs == null )
277             {
278                 return;
279             }
280
281             for ( ManagedRepositoryConfiguration repoConfig : managedRepoConfigs )
282             {
283                 ManagedRepository repo = putRepository( repoConfig, null );
284                 configRepoIds.add( repoConfig.getId( ) );
285                 if ( repo.supportsFeature( StagingRepositoryFeature.class ) )
286                 {
287                     StagingRepositoryFeature stagF = repo.getFeature( StagingRepositoryFeature.class ).get( );
288                     if ( stagF.getStagingRepository( ) != null )
289                     {
290                         configRepoIds.add( stagF.getStagingRepository( ).getId( ) );
291                     }
292                 }
293             }
294             List<String> toRemove = managedRepositories.keySet( ).stream( ).filter( id -> !configRepoIds.contains( id ) ).collect( Collectors.toList( ) );
295             for ( String id : toRemove )
296             {
297                 ManagedRepository removed = managedRepositories.remove( id );
298                 removed.close( );
299             }
300         }
301         catch ( Throwable e )
302         {
303             log.error( "Could not initialize repositories from config: {}", e.getMessage( ), e );
304             return;
305         }
306     }
307
308     private ManagedRepository createNewManagedRepository( RepositoryProvider provider, ManagedRepositoryConfiguration cfg ) throws RepositoryException
309     {
310         log.debug( "Creating repo {}", cfg.getId( ) );
311         ManagedRepository repo = provider.createManagedInstance( cfg );
312         repo.registerEventHandler( RepositoryEvent.ANY, this );
313         updateRepositoryReferences( provider, repo, cfg, null );
314         return repo;
315
316     }
317
318     private String getStagingId( String repoId )
319     {
320         return repoId + StagingRepositoryFeature.STAGING_REPO_POSTFIX;
321     }
322
323     @SuppressWarnings( "unchecked" )
324     private void updateRepositoryReferences( RepositoryProvider provider, ManagedRepository repo, ManagedRepositoryConfiguration cfg, Configuration configuration ) throws RepositoryException
325     {
326         log.debug( "Updating references of repo {}", repo.getId( ) );
327         if ( repo.supportsFeature( StagingRepositoryFeature.class ) )
328         {
329             StagingRepositoryFeature feature = repo.getFeature( StagingRepositoryFeature.class ).get( );
330             if ( feature.isStageRepoNeeded( ) && feature.getStagingRepository( ) == null )
331             {
332                 ManagedRepository stageRepo = getManagedRepository( getStagingId( repo.getId( ) ) );
333                 if ( stageRepo == null )
334                 {
335                     stageRepo = getStagingRepository( provider, cfg, configuration );
336                     managedRepositories.put( stageRepo.getId( ), stageRepo );
337                     if ( configuration != null )
338                     {
339                         replaceOrAddRepositoryConfig( provider.getManagedConfiguration( stageRepo ), configuration );
340                     }
341                     pushEvent( new LifecycleEvent( LifecycleEvent.REGISTERED, this, stageRepo ) );
342                 }
343                 feature.setStagingRepository( stageRepo );
344             }
345         }
346         if ( repo instanceof EditableManagedRepository )
347         {
348             EditableManagedRepository editableRepo = (EditableManagedRepository) repo;
349             if ( repo.getContent( ) == null )
350             {
351                 editableRepo.setContent( repositoryContentFactory.getManagedRepositoryContent( repo ) );
352                 editableRepo.getContent( ).setRepository( editableRepo );
353             }
354             log.debug( "Index repo: " + repo.hasIndex( ) );
355             if ( repo.hasIndex( ) && ( repo.getIndexingContext( ) == null || !repo.getIndexingContext( ).isOpen( ) ) )
356             {
357                 log.debug( "Creating indexing context for {}", repo.getId( ) );
358                 createIndexingContext( editableRepo );
359             }
360         }
361         repo.registerEventHandler( RepositoryEvent.ANY, this );
362     }
363
364     @Override
365     public ArchivaIndexManager getIndexManager( RepositoryType type )
366     {
367         return indexManagerFactory.getIndexManager( type );
368     }
369
370     @Override
371     public MetadataReader getMetadataReader( final RepositoryType type ) throws UnsupportedRepositoryTypeException
372     {
373         if ( metadataReaderList != null )
374         {
375             return metadataReaderList.stream( ).filter( mr -> mr.isValidForType( type ) ).findFirst( ).orElseThrow( ( ) -> new UnsupportedRepositoryTypeException( type ) );
376         }
377         else
378         {
379             throw new UnsupportedRepositoryTypeException( type );
380         }
381     }
382
383     private void createIndexingContext( EditableRepository editableRepo ) throws RepositoryException
384     {
385         if ( editableRepo.supportsFeature( IndexCreationFeature.class ) )
386         {
387             ArchivaIndexManager idxManager = getIndexManager( editableRepo.getType( ) );
388             try
389             {
390                 editableRepo.setIndexingContext( idxManager.createContext( editableRepo ) );
391                 idxManager.updateLocalIndexPath( editableRepo );
392             }
393             catch ( IndexCreationFailedException e )
394             {
395                 throw new RepositoryException( "Could not create index for repository " + editableRepo.getId( ) + ": " + e.getMessage( ), e );
396             }
397         }
398     }
399
400     private ManagedRepository getStagingRepository( RepositoryProvider provider, ManagedRepositoryConfiguration baseRepoCfg, Configuration configuration ) throws RepositoryException
401     {
402         ManagedRepository stageRepo = getManagedRepository( getStagingId( baseRepoCfg.getId( ) ) );
403         if ( stageRepo == null )
404         {
405             stageRepo = provider.createStagingInstance( baseRepoCfg );
406             if ( stageRepo.supportsFeature( StagingRepositoryFeature.class ) )
407             {
408                 stageRepo.getFeature( StagingRepositoryFeature.class ).get( ).setStageRepoNeeded( false );
409             }
410             ManagedRepositoryConfiguration stageCfg = provider.getManagedConfiguration( stageRepo );
411             updateRepositoryReferences( provider, stageRepo, stageCfg, configuration );
412         }
413         return stageRepo;
414     }
415
416
417     private void updateRemoteRepositoriesFromConfig( )
418     {
419         try
420         {
421             List<RemoteRepositoryConfiguration> remoteRepoConfigs =
422                 configurationHandler.getBaseConfiguration( ).getRemoteRepositories( );
423
424             if ( remoteRepoConfigs == null )
425             {
426                 return;
427             }
428             Set<String> repoIds = new HashSet<>( );
429             for ( RemoteRepositoryConfiguration repoConfig : remoteRepoConfigs )
430             {
431                 putRepository( repoConfig, null );
432                 repoIds.add( repoConfig.getId( ) );
433             }
434
435             List<String> toRemove = remoteRepositories.keySet( ).stream( ).filter( id -> !repoIds.contains( id ) ).collect( Collectors.toList( ) );
436             for ( String id : toRemove )
437             {
438                 RemoteRepository removed = remoteRepositories.remove( id );
439                 removed.close( );
440             }
441
442         }
443         catch ( Throwable e )
444         {
445             log.error( "Could not initialize remote repositories from config: {}", e.getMessage( ), e );
446             return;
447         }
448     }
449
450     private RemoteRepository createNewRemoteRepository( RepositoryProvider provider, RemoteRepositoryConfiguration cfg ) throws RepositoryException
451     {
452         log.debug( "Creating remote repo {}", cfg.getId( ) );
453         RemoteRepository repo = provider.createRemoteInstance( cfg );
454         updateRepositoryReferences( provider, repo, cfg, null );
455         return repo;
456
457     }
458
459     private void updateRepositoryReferences( RepositoryProvider provider, RemoteRepository repo, RemoteRepositoryConfiguration cfg, Configuration configuration ) throws RepositoryException
460     {
461         if ( repo instanceof EditableRemoteRepository && repo.getContent( ) == null )
462         {
463             EditableRemoteRepository editableRepo = (EditableRemoteRepository) repo;
464             editableRepo.setContent( repositoryContentFactory.getRemoteRepositoryContent( repo ) );
465             if ( repo.supportsFeature( IndexCreationFeature.class ) && repo.getIndexingContext( ) == null )
466             {
467                 createIndexingContext( editableRepo );
468             }
469         }
470         repo.registerEventHandler( RepositoryEvent.ANY, this );
471     }
472
473
474     /**
475      * Returns all repositories that are registered. There is no defined order of the returned repositories.
476      *
477      * @return a list of managed and remote repositories
478      */
479     @Override
480     public Collection<Repository> getRepositories( )
481     {
482         rwLock.readLock( ).lock( );
483         try
484         {
485             return Stream.concat( managedRepositories.values( ).stream( ), remoteRepositories.values( ).stream( ) ).collect( Collectors.toList( ) );
486         }
487         finally
488         {
489             rwLock.readLock( ).unlock( );
490         }
491     }
492
493     /**
494      * Returns only the managed repositories. There is no defined order of the returned repositories.
495      *
496      * @return a list of managed repositories
497      */
498     @Override
499     public Collection<ManagedRepository> getManagedRepositories( )
500     {
501         rwLock.readLock( ).lock( );
502         try
503         {
504             return uManagedRepository.values( );
505         }
506         finally
507         {
508             rwLock.readLock( ).unlock( );
509         }
510     }
511
512     /**
513      * Returns only the remote repositories. There is no defined order of the returned repositories.
514      *
515      * @return a list of remote repositories
516      */
517     @Override
518     public Collection<RemoteRepository> getRemoteRepositories( )
519     {
520         rwLock.readLock( ).lock( );
521         try
522         {
523             return uRemoteRepositories.values( );
524         }
525         finally
526         {
527             rwLock.readLock( ).unlock( );
528         }
529     }
530
531     @Override
532     public Collection<RepositoryGroup> getRepositoryGroups( )
533     {
534         rwLock.readLock( ).lock( );
535         try
536         {
537             return groupHandler.getAll( );
538         }
539         finally
540         {
541             rwLock.readLock( ).unlock( );
542         }
543     }
544
545     /**
546      * Returns the repository with the given id. The returned repository may be a managed or remote repository.
547      * It returns null, if no repository is registered with the given id.
548      *
549      * @param repoId the repository id
550      * @return the repository if found, otherwise null
551      */
552     @Override
553     public Repository getRepository( String repoId )
554     {
555         rwLock.readLock( ).lock( );
556         try
557         {
558             log.debug( "getRepository {}", repoId );
559             if ( managedRepositories.containsKey( repoId ) )
560             {
561                 log.debug( "Managed repo" );
562                 return managedRepositories.get( repoId );
563             }
564             else if ( remoteRepositories.containsKey( repoId ) )
565             {
566                 log.debug( "Remote repo" );
567                 return remoteRepositories.get( repoId );
568             }
569             else if ( groupHandler.has( repoId ) )
570             {
571                 return groupHandler.get( repoId );
572             }
573             else
574             {
575                 return null;
576             }
577         }
578         finally
579         {
580             rwLock.readLock( ).unlock( );
581         }
582     }
583
584     /**
585      * Convenience method, that returns the managed repository with the given id.
586      * It returns null, if no managed repository is registered with this id.
587      *
588      * @param repoId the repository id
589      * @return the managed repository if found, otherwise null
590      */
591     @Override
592     public ManagedRepository getManagedRepository( String repoId )
593     {
594         rwLock.readLock( ).lock( );
595         try
596         {
597             return managedRepositories.get( repoId );
598         }
599         finally
600         {
601             rwLock.readLock( ).unlock( );
602         }
603     }
604
605     /**
606      * Convenience method, that returns the remote repository with the given id.
607      * It returns null, if no remote repository is registered with this id.
608      *
609      * @param repoId the repository id
610      * @return the remote repository if found, otherwise null
611      */
612     @Override
613     public RemoteRepository getRemoteRepository( String repoId )
614     {
615         rwLock.readLock( ).lock( );
616         try
617         {
618             return remoteRepositories.get( repoId );
619         }
620         finally
621         {
622             rwLock.readLock( ).unlock( );
623         }
624     }
625
626     @Override
627     public RepositoryGroup getRepositoryGroup( String groupId )
628     {
629         rwLock.readLock( ).lock( );
630         try
631         {
632             return groupHandler.get( groupId );
633         }
634         finally
635         {
636             rwLock.readLock( ).unlock( );
637         }
638     }
639
640     @Override
641     public boolean hasRepository( String repoId )
642     {
643         return this.managedRepositories.containsKey( repoId ) || this.remoteRepositories.containsKey( repoId ) || groupHandler.has( repoId );
644     }
645
646     @Override
647     public boolean hasManagedRepository( String repoId )
648     {
649         return this.managedRepositories.containsKey( repoId );
650     }
651
652     @Override
653     public boolean hasRemoteRepository( String repoId )
654     {
655         return this.remoteRepositories.containsKey( repoId );
656     }
657
658     @Override
659     public boolean hasRepositoryGroup( String groupId )
660     {
661         return groupHandler.has( groupId );
662     }
663
664     protected void saveConfiguration( Configuration configuration ) throws IndeterminateConfigurationException, RegistryException
665     {
666         configurationHandler.save( configuration, ConfigurationHandler.REGISTRY_EVENT_TAG );
667     }
668
669     /**
670      * Adds a new repository to the current list, or replaces the repository definition with
671      * the same id, if it exists already.
672      * The change is saved to the configuration immediately.
673      *
674      * @param managedRepository the new repository.
675      * @throws RepositoryException if the new repository could not be saved to the configuration.
676      */
677     @Override
678     public ManagedRepository putRepository( ManagedRepository managedRepository ) throws RepositoryException
679     {
680         rwLock.writeLock( ).lock( );
681         try
682         {
683             final String id = managedRepository.getId( );
684             if ( remoteRepositories.containsKey( id ) )
685             {
686                 throw new RepositoryException( "There exists a remote repository with id " + id + ". Could not update with managed repository." );
687             }
688             ManagedRepository originRepo = managedRepositories.put( id, managedRepository );
689             try
690             {
691                 if ( originRepo != null && originRepo != managedRepository )
692                 {
693                     originRepo.close( );
694                 }
695                 RepositoryProvider provider = getProvider( managedRepository.getType( ) );
696                 ManagedRepositoryConfiguration newCfg = provider.getManagedConfiguration( managedRepository );
697                 Configuration configuration = configurationHandler.getBaseConfiguration( );
698                 updateRepositoryReferences( provider, managedRepository, newCfg, configuration );
699                 ManagedRepositoryConfiguration oldCfg = configuration.findManagedRepositoryById( id );
700                 if ( oldCfg != null )
701                 {
702                     configuration.removeManagedRepository( oldCfg );
703                 }
704                 configuration.addManagedRepository( newCfg );
705                 saveConfiguration( configuration );
706                 if ( originRepo != managedRepository )
707                 {
708                     pushEvent( new LifecycleEvent( LifecycleEvent.REGISTERED, this, managedRepository ) );
709                 }
710                 else
711                 {
712                     pushEvent( new LifecycleEvent( LifecycleEvent.UPDATED, this, managedRepository ) );
713                 }
714                 return managedRepository;
715             }
716             catch ( Exception e )
717             {
718                 // Rollback only partly, because repository is closed already
719                 if ( originRepo != null )
720                 {
721                     managedRepositories.put( id, originRepo );
722                 }
723                 else
724                 {
725                     managedRepositories.remove( id );
726                 }
727                 log.error( "Exception during configuration update {}", e.getMessage( ), e );
728                 throw new RepositoryException( "Could not save the configuration" + ( e.getMessage( ) == null ? "" : ": " + e.getMessage( ) ) );
729             }
730         }
731         finally
732         {
733             rwLock.writeLock( ).unlock( );
734         }
735     }
736
737     /**
738      * Adds a new repository or updates the repository with the same id, if it exists already.
739      * The configuration is saved immediately.
740      *
741      * @param managedRepositoryConfiguration the repository configuration
742      * @return the updated or created repository
743      * @throws RepositoryException if an error occurs, or the configuration is not valid.
744      */
745     @Override
746     public ManagedRepository putRepository( ManagedRepositoryConfiguration managedRepositoryConfiguration ) throws RepositoryException
747     {
748         rwLock.writeLock( ).lock( );
749         try
750         {
751             final String id = managedRepositoryConfiguration.getId( );
752             final RepositoryType repositoryType = RepositoryType.valueOf( managedRepositoryConfiguration.getType( ) );
753             Configuration configuration = configurationHandler.getBaseConfiguration( );
754             ManagedRepository repo = managedRepositories.get( id );
755             ManagedRepositoryConfiguration oldCfg = repo != null ? getProvider( repositoryType ).getManagedConfiguration( repo ) : null;
756             repo = putRepository( managedRepositoryConfiguration, configuration );
757             try
758             {
759                 saveConfiguration( configuration );
760             }
761             catch ( IndeterminateConfigurationException | RegistryException e )
762             {
763                 if ( oldCfg != null )
764                 {
765                     getProvider( repositoryType ).updateManagedInstance( (EditableManagedRepository) repo, oldCfg );
766                 }
767                 log.error( "Could not save the configuration for repository {}: {}", id, e.getMessage( ), e );
768                 throw new RepositoryException( "Could not save the configuration for repository " + id + ": " + e.getMessage( ) );
769             }
770             return repo;
771         }
772         finally
773         {
774             rwLock.writeLock( ).unlock( );
775         }
776
777     }
778
779     /**
780      * Adds a new repository or updates the repository with the same id. The given configuration object is updated, but
781      * the configuration is not saved.
782      *
783      * @param managedRepositoryConfiguration the new or changed managed repository configuration
784      * @param configuration                  the configuration object (may be <code>null</code>)
785      * @return the new or updated repository
786      * @throws RepositoryException if the configuration cannot be saved or updated
787      */
788     @Override
789     public ManagedRepository putRepository( ManagedRepositoryConfiguration managedRepositoryConfiguration, Configuration configuration ) throws RepositoryException
790     {
791         rwLock.writeLock( ).lock( );
792         try
793         {
794             final String id = managedRepositoryConfiguration.getId( );
795             final RepositoryType repoType = RepositoryType.valueOf( managedRepositoryConfiguration.getType( ) );
796             ManagedRepository repo;
797             boolean registeredNew = false;
798             repo = managedRepositories.get( id );
799             if ( repo != null && repo.isOpen( ) )
800             {
801                 if ( repo instanceof EditableManagedRepository )
802                 {
803                     getProvider( repoType ).updateManagedInstance( (EditableManagedRepository) repo, managedRepositoryConfiguration );
804                 }
805                 else
806                 {
807                     throw new RepositoryException( "The repository is not editable " + id );
808                 }
809             }
810             else
811             {
812                 repo = getProvider( repoType ).createManagedInstance( managedRepositoryConfiguration );
813                 managedRepositories.put( id, repo );
814                 registeredNew = true;
815             }
816             updateRepositoryReferences( getProvider( repoType ), repo, managedRepositoryConfiguration, configuration );
817             replaceOrAddRepositoryConfig( managedRepositoryConfiguration, configuration );
818             if ( registeredNew )
819             {
820                 pushEvent( new LifecycleEvent( LifecycleEvent.REGISTERED, this, repo ) );
821             }
822             else
823             {
824                 pushEvent( new LifecycleEvent( LifecycleEvent.UPDATED, this, repo ) );
825             }
826             return repo;
827         }
828         finally
829         {
830             rwLock.writeLock( ).unlock( );
831         }
832     }
833
834
835     /**
836      * Adds a new repository group to the current list, or replaces the repository group definition with
837      * the same id, if it exists already.
838      * The change is saved to the configuration immediately.
839      *
840      * @param repositoryGroup the new repository group.
841      * @throws RepositoryException if the new repository group could not be saved to the configuration.
842      */
843     @Override
844     public RepositoryGroup putRepositoryGroup( RepositoryGroup repositoryGroup ) throws RepositoryException
845     {
846         rwLock.writeLock( ).lock( );
847         try
848         {
849             if ( this.groupHandler == null )
850             {
851                 throw new RepositoryException( "Fatal error. RepositoryGroupHandler not registered!" );
852             }
853             return this.groupHandler.put( repositoryGroup );
854         }
855         finally
856         {
857             rwLock.writeLock( ).unlock( );
858         }
859     }
860
861     /**
862      * Adds a new repository group or updates the repository with the same id, if it exists already.
863      * The configuration is saved immediately.
864      *
865      * @param repositoryGroupConfiguration the repository configuration
866      * @return the updated or created repository
867      * @throws RepositoryException if an error occurs, or the configuration is not valid.
868      */
869     @Override
870     public RepositoryGroup putRepositoryGroup( RepositoryGroupConfiguration repositoryGroupConfiguration ) throws RepositoryException
871     {
872         rwLock.writeLock( ).lock( );
873         try
874         {
875             return groupHandler.put( repositoryGroupConfiguration );
876         }
877         finally
878         {
879             rwLock.writeLock( ).unlock( );
880         }
881
882     }
883
884     @Override
885     public CheckedResult<RepositoryGroup, Map<String, List<ValidationError>>> putRepositoryGroupAndValidate( RepositoryGroupConfiguration repositoryGroupConfiguration )
886         throws RepositoryException
887     {
888         rwLock.writeLock( ).lock( );
889         try
890         {
891             return groupHandler.putWithCheck( repositoryGroupConfiguration, groupHandler.getValidator() );
892         }
893         finally
894         {
895             rwLock.writeLock( ).unlock( );
896         }
897     }
898
899     /**
900      * Adds a new repository group or updates the repository group with the same id. The given configuration object is updated, but
901      * the configuration is not saved.
902      *
903      * @param repositoryGroupConfiguration The configuration of the new or changed repository group.
904      * @param configuration                The configuration object. If it is <code>null</code>, the configuration is not saved.
905      * @return The new or updated repository group
906      * @throws RepositoryException if the configuration cannot be saved or updated
907      */
908     @Override
909     public RepositoryGroup putRepositoryGroup( RepositoryGroupConfiguration repositoryGroupConfiguration, Configuration configuration ) throws RepositoryException
910     {
911         rwLock.writeLock( ).lock( );
912         try
913         {
914             return groupHandler.put( repositoryGroupConfiguration, configuration );
915         }
916         finally
917         {
918             rwLock.writeLock( ).unlock( );
919         }
920     }
921
922     private void replaceOrAddRepositoryConfig( ManagedRepositoryConfiguration managedRepositoryConfiguration, Configuration configuration )
923     {
924         if ( configuration != null )
925         {
926             ManagedRepositoryConfiguration oldCfg = configuration.findManagedRepositoryById( managedRepositoryConfiguration.getId( ) );
927             if ( oldCfg != null )
928             {
929                 configuration.removeManagedRepository( oldCfg );
930             }
931             configuration.addManagedRepository( managedRepositoryConfiguration );
932         }
933     }
934
935     private void replaceOrAddRepositoryConfig( RemoteRepositoryConfiguration remoteRepositoryConfiguration, Configuration configuration )
936     {
937         if ( configuration != null )
938         {
939             RemoteRepositoryConfiguration oldCfg = configuration.findRemoteRepositoryById( remoteRepositoryConfiguration.getId( ) );
940             if ( oldCfg != null )
941             {
942                 configuration.removeRemoteRepository( oldCfg );
943             }
944             configuration.addRemoteRepository( remoteRepositoryConfiguration );
945         }
946     }
947
948     private void replaceOrAddRepositoryConfig( RepositoryGroupConfiguration repositoryGroupConfiguration, Configuration configuration )
949     {
950         RepositoryGroupConfiguration oldCfg = configuration.findRepositoryGroupById( repositoryGroupConfiguration.getId( ) );
951         if ( oldCfg != null )
952         {
953             configuration.removeRepositoryGroup( oldCfg );
954         }
955         configuration.addRepositoryGroup( repositoryGroupConfiguration );
956     }
957
958     @Override
959     public RemoteRepository putRepository( RemoteRepository remoteRepository, Configuration configuration ) throws RepositoryException
960     {
961         rwLock.writeLock( ).lock( );
962         try
963         {
964             final String id = remoteRepository.getId( );
965             if ( managedRepositories.containsKey( id ) )
966             {
967                 throw new RepositoryException( "There exists a managed repository with id " + id + ". Could not update with remote repository." );
968             }
969             RemoteRepository originRepo = remoteRepositories.put( id, remoteRepository );
970             RemoteRepositoryConfiguration oldCfg = null;
971             RemoteRepositoryConfiguration newCfg;
972             try
973             {
974                 if ( originRepo != null && originRepo != remoteRepository )
975                 {
976                     originRepo.close( );
977                 }
978                 final RepositoryProvider provider = getProvider( remoteRepository.getType( ) );
979                 newCfg = provider.getRemoteConfiguration( remoteRepository );
980                 updateRepositoryReferences( provider, remoteRepository, newCfg, configuration );
981                 oldCfg = configuration.findRemoteRepositoryById( id );
982                 if ( oldCfg != null )
983                 {
984                     configuration.removeRemoteRepository( oldCfg );
985                 }
986                 configuration.addRemoteRepository( newCfg );
987                 if ( remoteRepository != originRepo )
988                 {
989                     pushEvent( new LifecycleEvent( LifecycleEvent.REGISTERED, this, remoteRepository ) );
990                 }
991                 else
992                 {
993                     pushEvent( new LifecycleEvent( LifecycleEvent.UPDATED, this, remoteRepository ) );
994                 }
995                 return remoteRepository;
996             }
997             catch ( Exception e )
998             {
999                 // Rollback
1000                 if ( originRepo != null )
1001                 {
1002                     remoteRepositories.put( id, originRepo );
1003                 }
1004                 else
1005                 {
1006                     remoteRepositories.remove( id );
1007                 }
1008                 if ( oldCfg != null )
1009                 {
1010                     RemoteRepositoryConfiguration cfg = configuration.findRemoteRepositoryById( id );
1011                     if ( cfg != null )
1012                     {
1013                         configuration.removeRemoteRepository( cfg );
1014                         configuration.addRemoteRepository( oldCfg );
1015                     }
1016                 }
1017                 log.error( "Error while adding remote repository {}", e.getMessage( ), e );
1018                 throw new RepositoryException( "Could not save the configuration" + ( e.getMessage( ) == null ? "" : ": " + e.getMessage( ) ) );
1019             }
1020         }
1021         finally
1022         {
1023             rwLock.writeLock( ).unlock( );
1024         }
1025     }
1026
1027     /**
1028      * Adds a remote repository, or overwrites the repository definition with the same id, if it exists already.
1029      * The modification is saved to the configuration immediately.
1030      *
1031      * @param remoteRepository the remote repository to add
1032      * @throws RepositoryException if an error occurs during configuration save
1033      */
1034     @Override
1035     public RemoteRepository putRepository( RemoteRepository remoteRepository ) throws RepositoryException
1036     {
1037         rwLock.writeLock( ).lock( );
1038         try
1039         {
1040             Configuration configuration = configurationHandler.getBaseConfiguration( );
1041             try
1042             {
1043                 RemoteRepository repo = putRepository( remoteRepository, configuration );
1044                 saveConfiguration( configuration );
1045                 return repo;
1046             }
1047             catch ( RegistryException | IndeterminateConfigurationException e )
1048             {
1049                 log.error( "Error while saving remote repository {}", e.getMessage( ), e );
1050                 throw new RepositoryException( "Could not save the configuration" + ( e.getMessage( ) == null ? "" : ": " + e.getMessage( ) ) );
1051             }
1052         }
1053         finally
1054         {
1055             rwLock.writeLock( ).unlock( );
1056         }
1057     }
1058
1059     /**
1060      * Adds a new repository or updates the repository with the same id, if it exists already.
1061      * The configuration is saved immediately.
1062      *
1063      * @param remoteRepositoryConfiguration the repository configuration
1064      * @return the updated or created repository
1065      * @throws RepositoryException if an error occurs, or the configuration is not valid.
1066      */
1067     @Override
1068     public RemoteRepository putRepository( RemoteRepositoryConfiguration remoteRepositoryConfiguration ) throws RepositoryException
1069     {
1070         rwLock.writeLock( ).lock( );
1071         try
1072         {
1073             final String id = remoteRepositoryConfiguration.getId( );
1074             final RepositoryType repositoryType = RepositoryType.valueOf( remoteRepositoryConfiguration.getType( ) );
1075             Configuration configuration = configurationHandler.getBaseConfiguration( );
1076             RemoteRepository repo = remoteRepositories.get( id );
1077             RemoteRepositoryConfiguration oldCfg = repo != null ? getProvider( repositoryType ).getRemoteConfiguration( repo ) : null;
1078             repo = putRepository( remoteRepositoryConfiguration, configuration );
1079             try
1080             {
1081                 saveConfiguration( configuration );
1082             }
1083             catch ( IndeterminateConfigurationException | RegistryException e )
1084             {
1085                 if ( oldCfg != null )
1086                 {
1087                     getProvider( repositoryType ).updateRemoteInstance( (EditableRemoteRepository) repo, oldCfg );
1088                 }
1089                 log.error( "Could not save the configuration for repository {}: {}", id, e.getMessage( ), e );
1090                 throw new RepositoryException( "Could not save the configuration for repository " + id + ": " + e.getMessage( ) );
1091             }
1092             return repo;
1093         }
1094         finally
1095         {
1096             rwLock.writeLock( ).unlock( );
1097         }
1098
1099     }
1100
1101     /**
1102      * Adds a new repository or updates the repository with the same id. The given configuration object is updated, but
1103      * the configuration is not saved.
1104      *
1105      * @param remoteRepositoryConfiguration the new or changed repository configuration
1106      * @param configuration                 the configuration object
1107      * @return the new or updated repository
1108      * @throws RepositoryException if the configuration cannot be saved or updated
1109      */
1110     @Override
1111     @SuppressWarnings( "unchecked" )
1112     public RemoteRepository putRepository( RemoteRepositoryConfiguration remoteRepositoryConfiguration, Configuration configuration ) throws RepositoryException
1113     {
1114         rwLock.writeLock( ).lock( );
1115         try
1116         {
1117             final String id = remoteRepositoryConfiguration.getId( );
1118             final RepositoryType repoType = RepositoryType.valueOf( remoteRepositoryConfiguration.getType( ) );
1119             RemoteRepository repo;
1120             boolean registeredNew = false;
1121             repo = remoteRepositories.get( id );
1122             if ( repo != null && repo.isOpen( ) )
1123             {
1124                 if ( repo instanceof EditableRemoteRepository )
1125                 {
1126                     getProvider( repoType ).updateRemoteInstance( (EditableRemoteRepository) repo, remoteRepositoryConfiguration );
1127                 }
1128                 else
1129                 {
1130                     throw new RepositoryException( "The repository is not editable " + id );
1131                 }
1132             }
1133             else
1134             {
1135                 repo = getProvider( repoType ).createRemoteInstance( remoteRepositoryConfiguration );
1136                 remoteRepositories.put( id, repo );
1137                 registeredNew = true;
1138             }
1139             updateRepositoryReferences( getProvider( repoType ), repo, remoteRepositoryConfiguration, configuration );
1140             replaceOrAddRepositoryConfig( remoteRepositoryConfiguration, configuration );
1141             if ( registeredNew )
1142             {
1143                 pushEvent( new LifecycleEvent( LifecycleEvent.REGISTERED, this, repo ) );
1144             }
1145             else
1146             {
1147                 pushEvent( new LifecycleEvent( LifecycleEvent.UPDATED, this, repo ) );
1148             }
1149             return repo;
1150         }
1151         finally
1152         {
1153             rwLock.writeLock( ).unlock( );
1154         }
1155
1156
1157     }
1158
1159     @Override
1160     public void removeRepository( String repoId ) throws RepositoryException
1161     {
1162         Repository repo = getRepository( repoId );
1163         if ( repo != null )
1164         {
1165             removeRepository( repo );
1166         }
1167     }
1168
1169     @Override
1170     public void removeRepository( Repository repo ) throws RepositoryException
1171     {
1172         if ( repo == null )
1173         {
1174             log.warn( "Trying to remove null repository" );
1175             return;
1176         }
1177         if ( repo instanceof RemoteRepository )
1178         {
1179             removeRepository( (RemoteRepository) repo );
1180         }
1181         else if ( repo instanceof ManagedRepository )
1182         {
1183             removeRepository( (ManagedRepository) repo );
1184         }
1185         else if ( repo instanceof RepositoryGroup )
1186         {
1187             removeRepositoryGroup( (RepositoryGroup) repo );
1188         }
1189         else
1190         {
1191             throw new RepositoryException( "Repository type not known: " + repo.getClass( ) );
1192         }
1193     }
1194
1195     /**
1196      * Removes a managed repository from the registry and configuration, if it exists.
1197      * The change is saved to the configuration immediately.
1198      *
1199      * @param managedRepository the managed repository to remove
1200      * @throws RepositoryException if a error occurs during configuration save
1201      */
1202     @Override
1203     public void removeRepository( ManagedRepository managedRepository ) throws RepositoryException
1204     {
1205         if ( managedRepository == null )
1206         {
1207             return;
1208         }
1209         final String id = managedRepository.getId( );
1210         ManagedRepository repo = getManagedRepository( id );
1211         if ( repo != null )
1212         {
1213             rwLock.writeLock( ).lock( );
1214             try
1215             {
1216                 repo = managedRepositories.remove( id );
1217                 if ( repo != null )
1218                 {
1219                     repo.close( );
1220                     this.groupHandler.removeRepositoryFromGroups( repo );
1221                     Configuration configuration = configurationHandler.getBaseConfiguration( );
1222                     ManagedRepositoryConfiguration cfg = configuration.findManagedRepositoryById( id );
1223                     if ( cfg != null )
1224                     {
1225                         configuration.removeManagedRepository( cfg );
1226                     }
1227                     saveConfiguration( configuration );
1228                 }
1229                 pushEvent( new LifecycleEvent( LifecycleEvent.UNREGISTERED, this, repo ) );
1230             }
1231             catch ( RegistryException | IndeterminateConfigurationException e )
1232             {
1233                 // Rollback
1234                 log.error( "Could not save config after repository removal: {}", e.getMessage( ), e );
1235                 managedRepositories.put( repo.getId( ), repo );
1236                 throw new RepositoryException( "Could not save configuration after repository removal: " + e.getMessage( ) );
1237             }
1238             finally
1239             {
1240                 rwLock.writeLock( ).unlock( );
1241             }
1242         }
1243     }
1244
1245
1246     @Override
1247     public void removeRepository( ManagedRepository managedRepository, Configuration configuration ) throws RepositoryException
1248     {
1249         if ( managedRepository == null )
1250         {
1251             return;
1252         }
1253         final String id = managedRepository.getId( );
1254         ManagedRepository repo = getManagedRepository( id );
1255         if ( repo != null )
1256         {
1257             rwLock.writeLock( ).lock( );
1258             try
1259             {
1260                 repo = managedRepositories.remove( id );
1261                 if ( repo != null )
1262                 {
1263                     repo.close( );
1264                     this.groupHandler.removeRepositoryFromGroups( repo );
1265                     ManagedRepositoryConfiguration cfg = configuration.findManagedRepositoryById( id );
1266                     if ( cfg != null )
1267                     {
1268                         configuration.removeManagedRepository( cfg );
1269                     }
1270                 }
1271                 pushEvent( new LifecycleEvent( LifecycleEvent.UNREGISTERED, this, repo ) );
1272             }
1273             finally
1274             {
1275                 rwLock.writeLock( ).unlock( );
1276             }
1277         }
1278
1279     }
1280
1281
1282     /**
1283      * Removes a repository group from the registry and configuration, if it exists.
1284      * The change is saved to the configuration immediately.
1285      *
1286      * @param repositoryGroup the repository group to remove
1287      * @throws RepositoryException if a error occurs during configuration save
1288      */
1289     @Override
1290     public void removeRepositoryGroup( RepositoryGroup repositoryGroup ) throws RepositoryException
1291     {
1292         if ( repositoryGroup == null )
1293         {
1294             return;
1295         }
1296         final String id = repositoryGroup.getId( );
1297         if ( groupHandler.has( id ) )
1298         {
1299             rwLock.writeLock( ).lock( );
1300             try
1301             {
1302                 groupHandler.remove( id );
1303             }
1304             finally
1305             {
1306                 rwLock.writeLock( ).unlock( );
1307             }
1308         }
1309     }
1310
1311     @Override
1312     public void removeRepositoryGroup( RepositoryGroup repositoryGroup, Configuration configuration ) throws RepositoryException
1313     {
1314         if ( repositoryGroup == null )
1315         {
1316             return;
1317         }
1318         final String id = repositoryGroup.getId( );
1319         if ( groupHandler.has( id ) )
1320         {
1321             rwLock.writeLock( ).lock( );
1322             try
1323             {
1324                 groupHandler.remove( id, configuration );
1325             }
1326             finally
1327             {
1328                 rwLock.writeLock( ).unlock( );
1329             }
1330         }
1331     }
1332
1333     private void doRemoveRepo( RemoteRepository repo, Configuration configuration )
1334     {
1335         repo.close( );
1336         RemoteRepositoryConfiguration cfg = configuration.findRemoteRepositoryById( repo.getId( ) );
1337         if ( cfg != null )
1338         {
1339             configuration.removeRemoteRepository( cfg );
1340         }
1341         List<ProxyConnectorConfiguration> proxyConnectors = new ArrayList<>( configuration.getProxyConnectors( ) );
1342         for ( ProxyConnectorConfiguration proxyConnector : proxyConnectors )
1343         {
1344             if ( StringUtils.equals( proxyConnector.getTargetRepoId( ), repo.getId( ) ) )
1345             {
1346                 configuration.removeProxyConnector( proxyConnector );
1347             }
1348         }
1349     }
1350
1351     /**
1352      * Removes the remote repository from the registry and configuration.
1353      * The change is saved to the configuration immediately.
1354      *
1355      * @param remoteRepository the remote repository to remove
1356      * @throws RepositoryException if a error occurs during configuration save
1357      */
1358     @Override
1359     public void removeRepository( RemoteRepository remoteRepository ) throws RepositoryException
1360     {
1361         if ( remoteRepository == null )
1362         {
1363             return;
1364         }
1365         final String id = remoteRepository.getId( );
1366         RemoteRepository repo = getRemoteRepository( id );
1367         if ( repo != null )
1368         {
1369             rwLock.writeLock( ).lock( );
1370             try
1371             {
1372                 repo = remoteRepositories.remove( id );
1373                 if ( repo != null )
1374                 {
1375                     Configuration configuration = configurationHandler.getBaseConfiguration( );
1376                     doRemoveRepo( repo, configuration );
1377                     saveConfiguration( configuration );
1378                 }
1379                 pushEvent( new LifecycleEvent( LifecycleEvent.UNREGISTERED, this, repo ) );
1380             }
1381             catch ( RegistryException | IndeterminateConfigurationException e )
1382             {
1383                 // Rollback
1384                 log.error( "Could not save config after repository removal: {}", e.getMessage( ), e );
1385                 remoteRepositories.put( repo.getId( ), repo );
1386                 throw new RepositoryException( "Could not save configuration after repository removal: " + e.getMessage( ) );
1387             }
1388             finally
1389             {
1390                 rwLock.writeLock( ).unlock( );
1391             }
1392         }
1393     }
1394
1395     @Override
1396     public void removeRepository( RemoteRepository remoteRepository, Configuration configuration ) throws RepositoryException
1397     {
1398         if ( remoteRepository == null )
1399         {
1400             return;
1401         }
1402         final String id = remoteRepository.getId( );
1403         RemoteRepository repo = getRemoteRepository( id );
1404         if ( repo != null )
1405         {
1406             rwLock.writeLock( ).lock( );
1407             try
1408             {
1409                 repo = remoteRepositories.remove( id );
1410                 if ( repo != null )
1411                 {
1412                     doRemoveRepo( repo, configuration );
1413                 }
1414                 pushEvent( new LifecycleEvent( LifecycleEvent.UNREGISTERED, this, repo ) );
1415             }
1416             finally
1417             {
1418                 rwLock.writeLock( ).unlock( );
1419             }
1420         }
1421
1422     }
1423
1424     /**
1425      * Reloads the registry from the configuration.
1426      */
1427     @Override
1428     public void reload( )
1429     {
1430         initialize( );
1431     }
1432
1433     /**
1434      * Resets the indexing context of a given repository.
1435      *
1436      * @param repository The repository
1437      * @throws IndexUpdateFailedException If the index could not be resetted.
1438      */
1439     @Override
1440     public void resetIndexingContext( Repository repository ) throws IndexUpdateFailedException
1441     {
1442         if ( repository.hasIndex( ) && repository instanceof EditableRepository )
1443         {
1444             EditableRepository eRepo = (EditableRepository) repository;
1445             ArchivaIndexingContext newCtx = getIndexManager( repository.getType( ) ).reset( repository.getIndexingContext( ) );
1446             eRepo.setIndexingContext( newCtx );
1447         }
1448     }
1449
1450
1451     /**
1452      * Creates a new repository instance with the same settings as this one. The cloned repository is not
1453      * registered or saved to the configuration.
1454      *
1455      * @param repo The origin repository
1456      * @return The cloned repository.
1457      */
1458     public ManagedRepository clone( ManagedRepository repo, String newId ) throws RepositoryException
1459     {
1460         if ( managedRepositories.containsKey( newId ) || remoteRepositories.containsKey( newId ) )
1461         {
1462             throw new RepositoryException( "The given id exists already " + newId );
1463         }
1464         RepositoryProvider provider = getProvider( repo.getType( ) );
1465         ManagedRepositoryConfiguration cfg = provider.getManagedConfiguration( repo );
1466         cfg.setId( newId );
1467         ManagedRepository cloned = provider.createManagedInstance( cfg );
1468         cloned.registerEventHandler( RepositoryEvent.ANY, this );
1469         return cloned;
1470     }
1471
1472     @Override
1473     public <T extends Repository> T clone( T repo, String newId ) throws RepositoryException
1474     {
1475         if ( repo instanceof RemoteRepository )
1476         {
1477             return (T) this.clone( (RemoteRepository) repo, newId );
1478         }
1479         else if ( repo instanceof ManagedRepository )
1480         {
1481             return (T) this.clone( (ManagedRepository) repo, newId );
1482         }
1483         else
1484         {
1485             throw new RepositoryException( "This repository class is not supported " + repo.getClass( ).getName( ) );
1486         }
1487     }
1488
1489     /**
1490      * Creates a new repository instance with the same settings as this one. The cloned repository is not
1491      * registered or saved to the configuration.
1492      *
1493      * @param repo The origin repository
1494      * @return The cloned repository.
1495      */
1496     public RemoteRepository clone( RemoteRepository repo, String newId ) throws RepositoryException
1497     {
1498         if ( managedRepositories.containsKey( newId ) || remoteRepositories.containsKey( newId ) )
1499         {
1500             throw new RepositoryException( "The given id exists already " + newId );
1501         }
1502         RepositoryProvider provider = getProvider( repo.getType( ) );
1503         RemoteRepositoryConfiguration cfg = provider.getRemoteConfiguration( repo );
1504         cfg.setId( newId );
1505         RemoteRepository cloned = provider.createRemoteInstance( cfg );
1506         cloned.registerEventHandler( RepositoryEvent.ANY, this );
1507         return cloned;
1508     }
1509
1510     @Override
1511     public Repository getRepositoryOfAsset( StorageAsset asset )
1512     {
1513         if ( asset instanceof Repository )
1514         {
1515             return (Repository) asset;
1516         }
1517         else
1518         {
1519             return getRepositories( ).stream( ).filter( r -> r.getRoot( )
1520                 .getStorage( ).equals( asset.getStorage( ) ) ).findFirst( ).orElse( null );
1521         }
1522     }
1523
1524     @Override
1525     public <R extends Repository> ValidationResponse<R> validateRepository( R repository )
1526     {
1527         Map<String, List<ValidationError>> errorMap = this.validators.stream( )
1528             .filter( ( validator ) -> validator.getType( ).equals( RepositoryType.ALL ) || repository.getType( ).equals( validator.getType( ) ) )
1529             .filter( val -> val.isFlavour( repository.getClass() ))
1530             .flatMap( validator -> ((RepositoryValidator<R>)validator).apply( repository ).getResult().entrySet( ).stream( ) )
1531             .collect( Collectors.toMap(
1532                 entry -> entry.getKey( ),
1533                 entry -> entry.getValue( ),
1534                 ( list1, list2 ) -> ListUtils.union( list1, list2 )
1535             ) );
1536         return new ValidationResponse( repository, errorMap );
1537     }
1538
1539     @Override
1540     public <R extends Repository> ValidationResponse<R> validateRepositoryForUpdate( R repository )
1541     {
1542         Map<String, List<ValidationError>> errorMap = this.validators.stream( )
1543             .filter( ( validator ) -> validator.getType( ).equals( RepositoryType.ALL ) || repository.getType( ).equals( validator.getType( ) ) )
1544             .filter( val -> val.isFlavour( repository.getClass() ))
1545             .flatMap( validator -> ((RepositoryValidator<R>)validator).applyForUpdate( repository ).getResult().entrySet( ).stream( ) )
1546             .collect( Collectors.toMap(
1547                 entry -> entry.getKey( ),
1548                 entry -> entry.getValue( ),
1549                 ( list1, list2 ) -> ListUtils.union( list1, list2 )
1550             ) );
1551         return new ValidationResponse( repository, errorMap );
1552     }
1553
1554     @Override
1555     public void configurationEvent( ConfigurationEvent event )
1556     {
1557         // We ignore the event, if the save was triggered by ourself
1558         if ( !ConfigurationHandler.REGISTRY_EVENT_TAG.equals( event.getTag( ) ) )
1559         {
1560             reload( );
1561         }
1562     }
1563
1564
1565     @Override
1566     public <T extends Event> void registerEventHandler( EventType<T> type, EventHandler<? super T> eventHandler )
1567     {
1568         eventManager.registerEventHandler( type, eventHandler );
1569     }
1570
1571
1572     @Override
1573     public <T extends Event> void unregisterEventHandler( EventType<T> type, EventHandler<? super T> eventHandler )
1574     {
1575         eventManager.unregisterEventHandler( type, eventHandler );
1576     }
1577
1578
1579     @Override
1580     public void handle( Event event )
1581     {
1582         // To avoid event cycles:
1583         if ( sameOriginator( event ) )
1584         {
1585             return;
1586         }
1587         if ( event instanceof RepositoryIndexEvent )
1588         {
1589             handleIndexCreationEvent( (RepositoryIndexEvent) event );
1590         }
1591         // We propagate all events to our listeners, but with context of repository registry
1592         pushEvent( event );
1593     }
1594
1595     private void handleIndexCreationEvent( RepositoryIndexEvent event )
1596     {
1597         RepositoryIndexEvent idxEvent = event;
1598         EditableRepository repo = (EditableRepository) idxEvent.getRepository( );
1599         if ( repo != null )
1600         {
1601             ArchivaIndexManager idxmgr = getIndexManager( repo.getType( ) );
1602             if ( repo.getIndexingContext( ) != null )
1603             {
1604                 try
1605                 {
1606                     ArchivaIndexingContext newCtx = idxmgr.move( repo.getIndexingContext( ), repo );
1607                     repo.setIndexingContext( newCtx );
1608                     idxmgr.updateLocalIndexPath( repo );
1609
1610                 }
1611                 catch ( IndexCreationFailedException e )
1612                 {
1613                     log.error( "Could not move index to new directory: '{}'", e.getMessage( ), e );
1614                 }
1615             }
1616             else
1617             {
1618                 try
1619                 {
1620                     ArchivaIndexingContext context = idxmgr.createContext( repo );
1621                     repo.setIndexingContext( context );
1622                     idxmgr.updateLocalIndexPath( repo );
1623                 }
1624                 catch ( IndexCreationFailedException e )
1625                 {
1626                     log.error( "Could not create index:  '{}'", e.getMessage( ), e );
1627                 }
1628             }
1629         }
1630     }
1631
1632     private boolean sameOriginator( Event event )
1633     {
1634         if ( event.getSource( ) == this )
1635         {
1636             return true;
1637         }
1638         else if ( event.hasPreviousEvent( ) )
1639         {
1640             return sameOriginator( event.getPreviousEvent( ) );
1641         }
1642         else
1643         {
1644             return false;
1645         }
1646     }
1647
1648     private void pushEvent( Event event )
1649     {
1650         eventManager.fireEvent( event );
1651     }
1652
1653
1654 }