]> source.dussan.org Git - archiva.git/blob
f4b02a2d650ded27248ecb5228280d0538a5c043
[archiva.git] /
1 package org.apache.archiva.repository.base;
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.RepositoryGroupConfiguration;
24 import org.apache.archiva.indexer.merger.MergedRemoteIndexesScheduler;
25 import org.apache.archiva.repository.EditableRepository;
26 import org.apache.archiva.repository.EditableRepositoryGroup;
27 import org.apache.archiva.repository.ManagedRepository;
28 import org.apache.archiva.repository.RepositoryException;
29 import org.apache.archiva.repository.RepositoryGroup;
30 import org.apache.archiva.repository.RepositoryProvider;
31 import org.apache.archiva.repository.RepositoryType;
32 import org.apache.archiva.repository.event.RepositoryEvent;
33 import org.apache.commons.lang3.StringUtils;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36 import org.springframework.stereotype.Service;
37
38 import javax.annotation.PostConstruct;
39 import javax.annotation.PreDestroy;
40 import javax.inject.Named;
41 import java.util.Collection;
42 import java.util.Collections;
43 import java.util.HashMap;
44 import java.util.LinkedHashMap;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.stream.Collectors;
48
49 import static org.apache.archiva.indexer.ArchivaIndexManager.DEFAULT_INDEX_PATH;
50
51 /**
52  * This class manages repository groups for the RepositoryRegistry.
53  * It is tightly coupled with the {@link ArchivaRepositoryRegistry}.
54  *
55  * @author Martin Stockhammer <martin_s@apache.org>
56  */
57 @Service("repositoryGroupHandler#default")
58 public class RepositoryGroupHandler
59 {
60     private static final Logger log = LoggerFactory.getLogger(RepositoryGroupHandler.class);
61
62     private final ArchivaRepositoryRegistry repositoryRegistry;
63     private final ConfigurationHandler configurationHandler;
64     private final MergedRemoteIndexesScheduler mergedRemoteIndexesScheduler;
65
66     private Map<String, RepositoryGroup> repositoryGroups = new HashMap<>();
67
68     /**
69      * Creates a new instance. All dependencies are injected on the constructor.
70      * @param repositoryRegistry the registry. To avoid circular dependencies via DI, this class registers itself on the registry.
71      * @param configurationHandler the configuration handler is used to retrieve and save configuration.
72      * @param mergedRemoteIndexesScheduler the index scheduler is used for merging the indexes from all group members
73      */
74     public RepositoryGroupHandler( ArchivaRepositoryRegistry repositoryRegistry,
75                                    ConfigurationHandler configurationHandler,
76                                    @Named("mergedRemoteIndexesScheduler#default") MergedRemoteIndexesScheduler mergedRemoteIndexesScheduler) {
77         this.configurationHandler = configurationHandler;
78         this.mergedRemoteIndexesScheduler = mergedRemoteIndexesScheduler;
79         this.repositoryRegistry = repositoryRegistry;
80     }
81
82     @PostConstruct
83     private void init() {
84         log.debug( "Initializing repository group handler " + repositoryRegistry.toString( ) );
85         // We are registering this class on the registry. This is necessary to avoid circular dependencies via injection.
86         this.repositoryRegistry.registerGroupHandler( this );
87     }
88
89     public void initializeFromConfig() {
90         this.repositoryGroups.clear();
91         this.repositoryGroups.putAll( getRepositorGroupsFromConfig( ) );
92     }
93
94     public Map<String, RepositoryGroup> getRepositorGroupsFromConfig() {
95         try {
96             List<RepositoryGroupConfiguration> repositoryGroupConfigurations =
97                 this.configurationHandler.getBaseConfiguration().getRepositoryGroups();
98
99             if (repositoryGroupConfigurations == null) {
100                 return Collections.emptyMap();
101             }
102
103             Map<String, RepositoryGroup> repositoryGroupMap = new LinkedHashMap<>(repositoryGroupConfigurations.size());
104
105             Map<RepositoryType, RepositoryProvider> providerMap = repositoryRegistry.getRepositoryProviderMap();
106             for (RepositoryGroupConfiguration repoConfig : repositoryGroupConfigurations) {
107                 RepositoryType repositoryType = RepositoryType.valueOf(repoConfig.getType());
108                 if (providerMap.containsKey(repositoryType)) {
109                     try {
110                         RepositoryGroup repo = createNewRepositoryGroup(providerMap.get(repositoryType), repoConfig);
111                         repositoryGroupMap.put(repo.getId(), repo);
112                     } catch (Exception e) {
113                         log.error("Could not create repository group {}: {}", repoConfig.getId(), e.getMessage(), e);
114                     }
115                 }
116             }
117             return repositoryGroupMap;
118         } catch (Throwable e) {
119             log.error("Could not initialize repositories from config: {}", e.getMessage(), e);
120             return Collections.emptyMap();
121         }
122     }
123
124     public RepositoryGroup createNewRepositoryGroup(RepositoryProvider provider, RepositoryGroupConfiguration config) throws RepositoryException
125     {
126         RepositoryGroup repositoryGroup = provider.createRepositoryGroup(config);
127         repositoryGroup.registerEventHandler( RepositoryEvent.ANY, repositoryRegistry);
128         updateRepositoryReferences(provider, repositoryGroup, config);
129         return repositoryGroup;
130     }
131
132     public void updateRepositoryReferences( RepositoryProvider provider, RepositoryGroup group, RepositoryGroupConfiguration configuration) {
133         if (group instanceof EditableRepositoryGroup ) {
134             EditableRepositoryGroup eGroup = (EditableRepositoryGroup) group;
135             eGroup.setRepositories(configuration.getRepositories().stream()
136                 .map(r -> repositoryRegistry.getManagedRepository(r)).collect( Collectors.toList()));
137         }
138     }
139
140     /**
141      * Adds a new repository group to the current list, or replaces the repository group definition with
142      * the same id, if it exists already.
143      * The change is saved to the configuration immediately.
144      *
145      * @param repositoryGroup the new repository group.
146      * @throws RepositoryException if the new repository group could not be saved to the configuration.
147      */
148     public RepositoryGroup putRepositoryGroup( RepositoryGroup repositoryGroup ) throws RepositoryException {
149             final String id = repositoryGroup.getId();
150             RepositoryGroup originRepoGroup = repositoryGroups.put(id, repositoryGroup);
151             try {
152                 if (originRepoGroup != null && originRepoGroup != repositoryGroup) {
153                     originRepoGroup.close();
154                 }
155                 RepositoryProvider provider = repositoryRegistry.getProvider( repositoryGroup.getType());
156                 RepositoryGroupConfiguration newCfg = provider.getRepositoryGroupConfiguration(repositoryGroup);
157                 Configuration configuration = this.configurationHandler.getBaseConfiguration();
158                 updateRepositoryReferences(provider, repositoryGroup, newCfg);
159                 RepositoryGroupConfiguration oldCfg = configuration.findRepositoryGroupById(id);
160                 if (oldCfg != null) {
161                     configuration.removeRepositoryGroup(oldCfg);
162                 }
163                 configuration.addRepositoryGroup(newCfg);
164                 repositoryRegistry.saveConfiguration(configuration);
165                 return repositoryGroup;
166             } catch (Exception e) {
167                 // Rollback
168                 if (originRepoGroup != null) {
169                     repositoryGroups.put(id, originRepoGroup);
170                 } else {
171                     repositoryGroups.remove(id);
172                 }
173                 log.error("Exception during configuration update {}", e.getMessage(), e);
174                 throw new RepositoryException("Could not save the configuration" + (e.getMessage() == null ? "" : ": " + e.getMessage()));
175             }
176     }
177
178     /**
179      * Adds a new repository group or updates the repository with the same id, if it exists already.
180      * The configuration is saved immediately.
181      *
182      * @param repositoryGroupConfiguration the repository configuration
183      * @return the updated or created repository
184      * @throws RepositoryException if an error occurs, or the configuration is not valid.
185      */
186     public RepositoryGroup putRepositoryGroup( RepositoryGroupConfiguration repositoryGroupConfiguration ) throws RepositoryException {
187             final String id = repositoryGroupConfiguration.getId();
188             final RepositoryType repositoryType = RepositoryType.valueOf(repositoryGroupConfiguration.getType());
189             Configuration configuration = this.configurationHandler.getBaseConfiguration();
190             RepositoryGroup repo = repositoryGroups.get(id);
191             RepositoryGroupConfiguration oldCfg = repo != null ? repositoryRegistry.getProvider(repositoryType).getRepositoryGroupConfiguration(repo) : null;
192             repo = putRepositoryGroup(repositoryGroupConfiguration, configuration);
193             try {
194                 repositoryRegistry.saveConfiguration(configuration);
195             } catch ( IndeterminateConfigurationException | RegistryException e) {
196                 if (oldCfg != null) {
197                     repositoryRegistry.getProvider(repositoryType).updateRepositoryGroupInstance((EditableRepositoryGroup) repo, oldCfg);
198                 }
199                 log.error("Could not save the configuration for repository group {}: {}", id, e.getMessage(), e);
200                 throw new RepositoryException("Could not save the configuration for repository group " + id + ": " + e.getMessage());
201             }
202             return repo;
203     }
204
205     public RepositoryGroup putRepositoryGroup( RepositoryGroupConfiguration repositoryGroupConfiguration, Configuration configuration ) throws RepositoryException {
206             final String id = repositoryGroupConfiguration.getId();
207             final RepositoryType repoType = RepositoryType.valueOf(repositoryGroupConfiguration.getType());
208             RepositoryGroup repo;
209             setRepositoryGroupDefaults(repositoryGroupConfiguration);
210             if (repositoryGroups.containsKey(id)) {
211                 repo = repositoryGroups.get(id);
212                 if (repo instanceof EditableRepositoryGroup) {
213                     repositoryRegistry.getProvider(repoType).updateRepositoryGroupInstance((EditableRepositoryGroup) repo, repositoryGroupConfiguration);
214                 } else {
215                     throw new RepositoryException("The repository is not editable " + id);
216                 }
217             } else {
218                 repo = repositoryRegistry.getProvider(repoType).createRepositoryGroup(repositoryGroupConfiguration);
219                 repositoryGroups.put(id, repo);
220             }
221             updateRepositoryReferences(repositoryRegistry.getProvider(repoType), repo, repositoryGroupConfiguration);
222             replaceOrAddRepositoryConfig(repositoryGroupConfiguration, configuration);
223             return repo;
224     }
225
226     private void setRepositoryGroupDefaults(RepositoryGroupConfiguration repositoryGroupConfiguration) {
227         if ( StringUtils.isEmpty(repositoryGroupConfiguration.getMergedIndexPath())) {
228             repositoryGroupConfiguration.setMergedIndexPath(DEFAULT_INDEX_PATH);
229         }
230         if (repositoryGroupConfiguration.getMergedIndexTtl() <= 0) {
231             repositoryGroupConfiguration.setMergedIndexTtl(300);
232         }
233         if (StringUtils.isEmpty(repositoryGroupConfiguration.getCronExpression())) {
234             repositoryGroupConfiguration.setCronExpression("0 0 03 ? * MON");
235         }
236     }
237
238     private void replaceOrAddRepositoryConfig(RepositoryGroupConfiguration repositoryGroupConfiguration, Configuration configuration) {
239         RepositoryGroupConfiguration oldCfg = configuration.findRepositoryGroupById(repositoryGroupConfiguration.getId());
240         if (oldCfg != null) {
241             configuration.removeRepositoryGroup(oldCfg);
242         }
243         configuration.addRepositoryGroup(repositoryGroupConfiguration);
244     }
245
246     public void removeRepositoryFromGroups( ManagedRepository repo) {
247         if (repo != null) {
248             repositoryGroups.values().stream().filter(repoGroup -> repoGroup instanceof EditableRepository ).
249                 map(repoGroup -> (EditableRepositoryGroup) repoGroup).forEach(repoGroup -> repoGroup.removeRepository(repo));
250         }
251     }
252
253     /**
254      * Removes a repository group from the registry and configuration, if it exists.
255      * The change is saved to the configuration immediately.
256      *
257      * @param id the id of the repository group to remove
258      * @throws RepositoryException if a error occurs during configuration save
259      */
260     public void removeRepositoryGroup( final String id ) throws RepositoryException {
261         RepositoryGroup repo = getRepositoryGroup(id);
262         if (repo != null) {
263             try {
264                 repo = repositoryGroups.remove(id);
265                 if (repo != null) {
266                     repo.close();
267                     Configuration configuration = this.configurationHandler.getBaseConfiguration();
268                     RepositoryGroupConfiguration cfg = configuration.findRepositoryGroupById(id);
269                     if (cfg != null) {
270                         configuration.removeRepositoryGroup(cfg);
271                     }
272                     this.configurationHandler.save(configuration, ConfigurationHandler.REGISTRY_EVENT_TAG );
273                 }
274
275             } catch (RegistryException | IndeterminateConfigurationException e) {
276                 // Rollback
277                 log.error("Could not save config after repository removal: {}", e.getMessage(), e);
278                 repositoryGroups.put(repo.getId(), repo);
279                 throw new RepositoryException("Could not save configuration after repository removal: " + e.getMessage());
280             }
281         }
282     }
283
284     public void removeRepositoryGroup( String id, Configuration configuration ) throws RepositoryException {
285         RepositoryGroup repo = repositoryGroups.get(id);
286         if (repo != null) {
287                 repo = repositoryGroups.remove(id);
288                 if (repo != null) {
289                     repo.close();
290                     RepositoryGroupConfiguration cfg = configuration.findRepositoryGroupById(id);
291                     if (cfg != null) {
292                         configuration.removeRepositoryGroup(cfg);
293                     }
294                 }
295         }
296
297     }
298
299     public RepositoryGroup getRepositoryGroup( String groupId ) {
300         return repositoryGroups.get(groupId);
301     }
302
303     public Collection<RepositoryGroup> getRepositoryGroups() {
304         return repositoryGroups.values( );
305     }
306
307     public boolean hasRepositoryGroup(String id) {
308         return repositoryGroups.containsKey( id );
309     }
310
311     @PreDestroy
312     private void destroy() {
313         this.close( );
314     }
315
316     public void close() {
317         for (RepositoryGroup group : repositoryGroups.values()) {
318             try
319             {
320                 group.close( );
321             } catch (Throwable e) {
322                 log.error( "Could not close repository group {}: {}", group.getId( ), e.getMessage( ) );
323             }
324         }
325         this.repositoryGroups.clear();
326     }
327
328 }