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