]> source.dussan.org Git - archiva.git/blob
9285de888addd31ac236499fbbbea293e2c51e9d
[archiva.git] /
1 package org.apache.archiva.repository.base.group;
2 /*
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied.  See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19
20 import org.apache.archiva.components.registry.RegistryException;
21 import org.apache.archiva.configuration.Configuration;
22 import org.apache.archiva.configuration.IndeterminateConfigurationException;
23 import org.apache.archiva.configuration.ManagedRepositoryConfiguration;
24 import org.apache.archiva.configuration.RepositoryGroupConfiguration;
25 import org.apache.archiva.indexer.merger.MergedRemoteIndexesScheduler;
26 import org.apache.archiva.repository.EditableRepository;
27 import org.apache.archiva.repository.EditableRepositoryGroup;
28 import org.apache.archiva.repository.ManagedRepository;
29 import org.apache.archiva.repository.Repository;
30 import org.apache.archiva.repository.RepositoryException;
31 import org.apache.archiva.repository.RepositoryGroup;
32 import org.apache.archiva.repository.RepositoryHandler;
33 import org.apache.archiva.repository.RepositoryHandlerManager;
34 import org.apache.archiva.repository.RepositoryProvider;
35 import org.apache.archiva.repository.RepositoryState;
36 import org.apache.archiva.repository.RepositoryType;
37 import org.apache.archiva.repository.base.AbstractRepositoryHandler;
38 import org.apache.archiva.repository.base.ArchivaRepositoryRegistry;
39 import org.apache.archiva.repository.base.ConfigurationHandler;
40 import org.apache.archiva.repository.event.LifecycleEvent;
41 import org.apache.archiva.repository.event.RepositoryEvent;
42 import org.apache.archiva.repository.features.IndexCreationFeature;
43 import org.apache.archiva.repository.storage.StorageAsset;
44 import org.apache.commons.lang3.StringUtils;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47 import org.springframework.stereotype.Service;
48
49 import javax.annotation.PostConstruct;
50 import javax.annotation.PreDestroy;
51 import javax.inject.Named;
52 import java.io.IOException;
53 import java.nio.file.Files;
54 import java.nio.file.Path;
55 import java.util.Collections;
56 import java.util.LinkedHashMap;
57 import java.util.List;
58 import java.util.Map;
59 import java.util.concurrent.locks.ReentrantReadWriteLock;
60 import java.util.stream.Collectors;
61
62 import static org.apache.archiva.indexer.ArchivaIndexManager.DEFAULT_INDEX_PATH;
63
64 /**
65  * This class manages repository groups for the RepositoryRegistry.
66  * It is tightly coupled with the {@link ArchivaRepositoryRegistry}.
67  *
68  * @author Martin Stockhammer <martin_s@apache.org>
69  */
70 @Service( "repositoryGroupHandler#default" )
71 public class RepositoryGroupHandler
72     extends AbstractRepositoryHandler<RepositoryGroup, RepositoryGroupConfiguration>
73     implements RepositoryHandler<RepositoryGroup, RepositoryGroupConfiguration>
74 {
75     private static final Logger log = LoggerFactory.getLogger( RepositoryGroupHandler.class );
76
77     private final RepositoryHandlerManager repositoryRegistry;
78     private final MergedRemoteIndexesScheduler mergedRemoteIndexesScheduler;
79
80     private Path groupsDirectory;
81
82
83     /**
84      * Creates a new instance. All dependencies are injected on the constructor.
85      *
86      * @param repositoryRegistry           the registry. To avoid circular dependencies via DI, this class registers itself on the registry.
87      * @param configurationHandler         the configuration handler is used to retrieve and save configuration.
88      * @param mergedRemoteIndexesScheduler the index scheduler is used for merging the indexes from all group members
89      */
90     public RepositoryGroupHandler( RepositoryHandlerManager repositoryRegistry,
91                                    ConfigurationHandler configurationHandler,
92                                    @Named( "mergedRemoteIndexesScheduler#default" ) MergedRemoteIndexesScheduler mergedRemoteIndexesScheduler
93                                    )
94     {
95         super( RepositoryGroup.class, RepositoryGroupConfiguration.class, configurationHandler );
96         this.mergedRemoteIndexesScheduler = mergedRemoteIndexesScheduler;
97         this.repositoryRegistry = repositoryRegistry;
98     }
99
100     @Override
101     @PostConstruct
102     public void init( )
103     {
104         log.debug( "Initializing repository group handler " + repositoryRegistry.toString( ) );
105         initializeStorage( );
106         // We are registering this class on the registry. This is necessary to avoid circular dependencies via injection.
107         this.repositoryRegistry.registerHandler( this );
108     }
109
110     @Override
111     public void initializeFromConfig( )
112     {
113         getRepositories().clear( );
114         getRepositories().putAll( newInstancesFromConfig( ) );
115         for ( RepositoryGroup group : getRepositories().values( ) )
116         {
117             activateRepository( group );
118         }
119     }
120
121     private void initializeStorage( )
122     {
123         Path baseDir = this.getConfigurationHandler().getArchivaConfiguration( ).getRepositoryGroupBaseDir( );
124         if ( !Files.exists( baseDir ) )
125         {
126             try
127             {
128                 Files.createDirectories( baseDir );
129             }
130             catch ( IOException e )
131             {
132                 log.error( "Could not create group base directory: {}", e.getMessage( ), e );
133             }
134         }
135         this.groupsDirectory = baseDir;
136     }
137
138     @Override
139     public void activateRepository( RepositoryGroup repositoryGroup )
140     {
141         StorageAsset indexDirectory = getMergedIndexDirectory( repositoryGroup );
142         if ( !indexDirectory.exists( ) )
143         {
144             try
145             {
146                 indexDirectory.create( );
147             }
148             catch ( IOException e )
149             {
150                 log.error( "Could not create index directory {} for group {}: {}", indexDirectory, repositoryGroup.getId( ), e.getMessage( ) );
151             }
152         }
153         Path groupPath = groupsDirectory.resolve( repositoryGroup.getId( ) );
154         if ( !Files.exists( groupPath ) )
155         {
156             try
157             {
158                 Files.createDirectories( groupPath );
159             }
160             catch ( IOException e )
161             {
162                 log.error( "Could not create repository group directory {}", groupPath );
163             }
164         }
165         mergedRemoteIndexesScheduler.schedule( repositoryGroup,
166             indexDirectory );
167         setLastState( repositoryGroup, RepositoryState.INITIALIZED );
168     }
169
170     @Override
171     public void deactivateRepository( RepositoryGroup repository )
172     {
173         mergedRemoteIndexesScheduler.unschedule( repository );
174         repository.close();
175         setLastState( repository, RepositoryState.DEACTIVATED );
176     }
177
178     public StorageAsset getMergedIndexDirectory( RepositoryGroup group )
179     {
180         if ( group != null )
181         {
182             return group.getFeature( IndexCreationFeature.class ).get( ).getLocalIndexPath( );
183         }
184         else
185         {
186             return null;
187         }
188     }
189
190
191     @Override
192     public Map<String, RepositoryGroup> newInstancesFromConfig( )
193     {
194         try
195         {
196             List<RepositoryGroupConfiguration> repositoryGroupConfigurations =
197                 this.getConfigurationHandler().getBaseConfiguration( ).getRepositoryGroups( );
198
199             if ( repositoryGroupConfigurations == null )
200             {
201                 return Collections.emptyMap( );
202             }
203
204             Map<String, RepositoryGroup> repositoryGroupMap = new LinkedHashMap<>( repositoryGroupConfigurations.size( ) );
205
206             for ( RepositoryGroupConfiguration repoConfig : repositoryGroupConfigurations )
207             {
208                 RepositoryType repositoryType = RepositoryType.valueOf( repoConfig.getType( ) );
209                 if ( super.providerMap.containsKey( repositoryType ) )
210                 {
211                     try
212                     {
213                         RepositoryGroup repo = createNewRepositoryGroup( providerMap.get( repositoryType ), repoConfig );
214                         repositoryGroupMap.put( repo.getId( ), repo );
215                     }
216                     catch ( Exception e )
217                     {
218                         log.error( "Could not create repository group {}: {}", repoConfig.getId( ), e.getMessage( ), e );
219                     }
220                 }
221             }
222             return repositoryGroupMap;
223         }
224         catch ( Throwable e )
225         {
226             log.error( "Could not initialize repositories from config: {}", e.getMessage( ), e );
227             return Collections.emptyMap( );
228         }
229     }
230
231     @Override
232     public RepositoryGroup newInstance( final RepositoryType type, String id ) throws RepositoryException
233     {
234         RepositoryProvider provider = getProvider( type );
235         RepositoryGroupConfiguration config = new RepositoryGroupConfiguration( );
236         config.setId( id );
237         return createNewRepositoryGroup( provider, config );
238     }
239
240     @Override
241     public RepositoryGroup newInstance( final RepositoryGroupConfiguration repositoryConfiguration ) throws RepositoryException
242     {
243         RepositoryType type = RepositoryType.valueOf( repositoryConfiguration.getType( ) );
244         RepositoryProvider provider = getProvider( type );
245         return createNewRepositoryGroup( provider, repositoryConfiguration );
246     }
247
248     private RepositoryGroup createNewRepositoryGroup( RepositoryProvider provider, RepositoryGroupConfiguration config ) throws RepositoryException
249     {
250         RepositoryGroup repositoryGroup = provider.createRepositoryGroup( config );
251         updateReferences( repositoryGroup, config );
252         if (repositoryGroup instanceof EditableRepository)
253         {
254             ( (EditableRepository) repositoryGroup ).setLastState( RepositoryState.REFERENCES_SET );
255         }
256         repositoryGroup.registerEventHandler( RepositoryEvent.ANY, repositoryRegistry );
257         return repositoryGroup;
258     }
259
260     /**
261      * Adds a new repository group to the current list, or replaces the repository group definition with
262      * the same id, if it exists already.
263      * The change is saved to the configuration immediately.
264      *
265      * @param repositoryGroup the new repository group.
266      * @throws RepositoryException if the new repository group could not be saved to the configuration.
267      */
268     @Override
269     public RepositoryGroup put( final RepositoryGroup repositoryGroup ) throws RepositoryException
270     {
271         final String id = repositoryGroup.getId( );
272         RepositoryGroup originRepoGroup = getRepositories().remove( id );
273         try
274         {
275             if ( originRepoGroup != null && originRepoGroup != repositoryGroup )
276             {
277                 deactivateRepository( originRepoGroup );
278                 pushEvent( new LifecycleEvent( LifecycleEvent.UNREGISTERED, this, originRepoGroup ) );
279             }
280             RepositoryProvider provider = getProvider( repositoryGroup.getType( ) );
281             RepositoryGroupConfiguration newCfg = provider.getRepositoryGroupConfiguration( repositoryGroup );
282             ReentrantReadWriteLock.WriteLock configLock = this.getConfigurationHandler().getLock( ).writeLock( );
283             configLock.lock( );
284             try
285             {
286                 Configuration configuration = this.getConfigurationHandler().getBaseConfiguration( );
287                 updateReferences( repositoryGroup, newCfg );
288                 RepositoryGroupConfiguration oldCfg = configuration.findRepositoryGroupById( id );
289                 if ( oldCfg != null )
290                 {
291                     configuration.removeRepositoryGroup( oldCfg );
292                 }
293                 configuration.addRepositoryGroup( newCfg );
294                 getConfigurationHandler().save( configuration, ConfigurationHandler.REGISTRY_EVENT_TAG );
295                 setLastState( repositoryGroup, RepositoryState.SAVED );
296                 activateRepository( repositoryGroup );
297             }
298             finally
299             {
300                 configLock.unlock( );
301             }
302             getRepositories().put( id, repositoryGroup );
303             setLastState( repositoryGroup, RepositoryState.REGISTERED );
304             return repositoryGroup;
305         }
306         catch ( Exception e )
307         {
308             // Rollback
309             if ( originRepoGroup != null )
310             {
311                 getRepositories().put( id, originRepoGroup );
312             }
313             else
314             {
315                 getRepositories().remove( id );
316             }
317             log.error( "Exception during configuration update {}", e.getMessage( ), e );
318             throw new RepositoryException( "Could not save the configuration" + ( e.getMessage( ) == null ? "" : ": " + e.getMessage( ) ), e);
319         }
320     }
321
322     /**
323      * Adds a new repository group or updates the repository with the same id, if it exists already.
324      * The configuration is saved immediately.
325      *
326      * @param repositoryGroupConfiguration the repository configuration
327      * @return the updated or created repository
328      * @throws RepositoryException if an error occurs, or the configuration is not valid.
329      */
330     @Override
331     public RepositoryGroup put( RepositoryGroupConfiguration repositoryGroupConfiguration ) throws RepositoryException
332     {
333         final String id = repositoryGroupConfiguration.getId( );
334         final RepositoryType repositoryType = RepositoryType.valueOf( repositoryGroupConfiguration.getType( ) );
335         final RepositoryProvider provider = getProvider( repositoryType );
336         RepositoryGroup currentRepository;
337         ReentrantReadWriteLock.WriteLock configLock = this.getConfigurationHandler().getLock( ).writeLock( );
338         configLock.lock( );
339         try
340         {
341             Configuration configuration = this.getConfigurationHandler().getBaseConfiguration( );
342             currentRepository = getRepositories().get( id );
343             RepositoryGroup oldRepository = currentRepository == null ? null : clone( currentRepository, id );
344             try
345             {
346                 boolean updated = false;
347                 if (currentRepository==null) {
348                     currentRepository = put( repositoryGroupConfiguration, configuration );
349                 } else
350                 {
351                     setRepositoryGroupDefaults( repositoryGroupConfiguration );
352                     provider.updateRepositoryGroupInstance( (EditableRepositoryGroup) currentRepository, repositoryGroupConfiguration );
353                     updated = true;
354                     pushEvent( LifecycleEvent.UPDATED, currentRepository );
355                 }
356                 registerNewRepository( repositoryGroupConfiguration, currentRepository, configuration, updated );
357             }
358             catch ( IndeterminateConfigurationException | RegistryException | RepositoryException e )
359             {
360                 // Trying a rollback
361                 if ( oldRepository != null  )
362                 {
363                     RepositoryGroupConfiguration oldCfg = provider.getRepositoryGroupConfiguration( oldRepository );
364                     provider.updateRepositoryGroupInstance( (EditableRepositoryGroup) currentRepository, oldCfg );
365                     rollback( configuration, oldRepository, e, oldCfg );
366                 } else {
367                     getRepositories().remove( id );
368                 }
369                 log.error( "Could not save the configuration for repository group {}: {}", id, e.getMessage( ), e );
370                 if (e instanceof RepositoryException) {
371                     throw (RepositoryException) e;
372                 } else
373                 {
374                     throw new RepositoryException( "Could not save the configuration for repository group " + id + ": " + e.getMessage( ) );
375                 }
376             }
377         }
378         finally
379         {
380             configLock.unlock( );
381         }
382         return currentRepository;
383     }
384
385     @Override
386     public RepositoryGroup put( RepositoryGroupConfiguration repositoryGroupConfiguration, Configuration configuration ) throws RepositoryException
387     {
388         final String id = repositoryGroupConfiguration.getId( );
389         final RepositoryType repoType = RepositoryType.valueOf( repositoryGroupConfiguration.getType( ) );
390         RepositoryGroup repo;
391         setRepositoryGroupDefaults( repositoryGroupConfiguration );
392         if ( getRepositories().containsKey( id ) )
393         {
394             repo = clone( getRepositories().get( id ), id );
395             if ( repo instanceof EditableRepositoryGroup )
396             {
397                 getProvider( repoType ).updateRepositoryGroupInstance( (EditableRepositoryGroup) repo, repositoryGroupConfiguration );
398             }
399             else
400             {
401                 throw new RepositoryException( "The repository is not editable " + id );
402             }
403         }
404         else
405         {
406             repo = getProvider( repoType ).createRepositoryGroup( repositoryGroupConfiguration );
407             setLastState( repo, RepositoryState.CREATED );
408         }
409         replaceOrAddRepositoryConfig( repositoryGroupConfiguration, configuration );
410         updateReferences( repo, repositoryGroupConfiguration );
411         setLastState( repo, RepositoryState.REFERENCES_SET );
412         return repo;
413     }
414
415     private void setRepositoryGroupDefaults( RepositoryGroupConfiguration repositoryGroupConfiguration )
416     {
417         if ( StringUtils.isEmpty( repositoryGroupConfiguration.getMergedIndexPath( ) ) )
418         {
419             repositoryGroupConfiguration.setMergedIndexPath( DEFAULT_INDEX_PATH );
420         }
421         if ( repositoryGroupConfiguration.getMergedIndexTtl( ) <= 0 )
422         {
423             repositoryGroupConfiguration.setMergedIndexTtl( 300 );
424         }
425         if ( StringUtils.isEmpty( repositoryGroupConfiguration.getCronExpression( ) ) )
426         {
427             repositoryGroupConfiguration.setCronExpression( "0 0 03 ? * MON" );
428         }
429     }
430
431     @Override
432     public void processOtherVariantRemoval( Repository repo )
433     {
434         if ( repo instanceof ManagedRepository )
435         {
436             getRepositories().values( ).stream( ).filter( repoGroup -> repoGroup instanceof EditableRepository ).
437                 map( repoGroup -> (EditableRepositoryGroup) repoGroup ).forEach( repoGroup -> repoGroup.removeRepository( (ManagedRepository) repo ) );
438         }
439     }
440
441
442     @Override
443     public RepositoryGroup clone( RepositoryGroup repo, String newId ) throws RepositoryException
444     {
445         RepositoryProvider provider = getProvider( repo.getType( ) );
446         RepositoryGroupConfiguration cfg = provider.getRepositoryGroupConfiguration( repo );
447         cfg.setId( newId );
448         RepositoryGroup cloned = provider.createRepositoryGroup( cfg );
449         cloned.registerEventHandler( RepositoryEvent.ANY, repositoryRegistry );
450         setLastState( cloned, RepositoryState.CREATED );
451         return cloned;
452     }
453
454     @Override
455     public void updateReferences( RepositoryGroup repo, RepositoryGroupConfiguration repositoryConfiguration )
456     {
457         if ( repo instanceof EditableRepositoryGroup && repositoryConfiguration!=null)
458         {
459             EditableRepositoryGroup eGroup = (EditableRepositoryGroup) repo;
460             RepositoryHandler<ManagedRepository, ManagedRepositoryConfiguration> managedHandler = repositoryRegistry.getHandler( ManagedRepository.class, ManagedRepositoryConfiguration.class );
461             eGroup.setRepositories( repositoryConfiguration.getRepositories( ).stream( )
462                 .map( managedHandler::get ).collect( Collectors.toList( ) ) );
463         }
464
465     }
466
467
468     @PreDestroy
469     private void destroy( )
470     {
471         this.close( );
472     }
473
474     @Override
475     protected RepositoryGroupConfiguration findRepositoryConfiguration( Configuration configuration, String id )
476     {
477         return configuration.findRepositoryGroupById( id );
478     }
479
480     @Override
481     protected void removeRepositoryConfiguration( Configuration configuration, RepositoryGroupConfiguration cfg )
482     {
483         configuration.removeRepositoryGroup( cfg );
484     }
485
486     @Override
487     protected void addRepositoryConfiguration( Configuration configuration, RepositoryGroupConfiguration repoConfiguration )
488     {
489         configuration.addRepositoryGroup( repoConfiguration );
490     }
491 }