1 package org.apache.archiva.repository.base;
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
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
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;
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;
47 import java.util.stream.Collectors;
49 import static org.apache.archiva.indexer.ArchivaIndexManager.DEFAULT_INDEX_PATH;
52 * This class manages repository groups for the RepositoryRegistry.
53 * It is tightly coupled with the {@link ArchivaRepositoryRegistry}.
55 * @author Martin Stockhammer <martin_s@apache.org>
57 @Service("repositoryGroupHandler#default")
58 public class RepositoryGroupHandler
60 private static final Logger log = LoggerFactory.getLogger(RepositoryGroupHandler.class);
62 private final ArchivaRepositoryRegistry repositoryRegistry;
63 private final ConfigurationHandler configurationHandler;
64 private final MergedRemoteIndexesScheduler mergedRemoteIndexesScheduler;
66 private Map<String, RepositoryGroup> repositoryGroups = new HashMap<>();
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
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;
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 );
89 public void initializeFromConfig() {
90 this.repositoryGroups.clear();
91 this.repositoryGroups.putAll( getRepositorGroupsFromConfig( ) );
94 public Map<String, RepositoryGroup> getRepositorGroupsFromConfig() {
96 List<RepositoryGroupConfiguration> repositoryGroupConfigurations =
97 this.configurationHandler.getBaseConfiguration().getRepositoryGroups();
99 if (repositoryGroupConfigurations == null) {
100 return Collections.emptyMap();
103 Map<String, RepositoryGroup> repositoryGroupMap = new LinkedHashMap<>(repositoryGroupConfigurations.size());
105 Map<RepositoryType, RepositoryProvider> providerMap = repositoryRegistry.getRepositoryProviderMap();
106 for (RepositoryGroupConfiguration repoConfig : repositoryGroupConfigurations) {
107 RepositoryType repositoryType = RepositoryType.valueOf(repoConfig.getType());
108 if (providerMap.containsKey(repositoryType)) {
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);
117 return repositoryGroupMap;
118 } catch (Throwable e) {
119 log.error("Could not initialize repositories from config: {}", e.getMessage(), e);
120 return Collections.emptyMap();
124 public RepositoryGroup createNewRepositoryGroup(RepositoryProvider provider, RepositoryGroupConfiguration config) throws RepositoryException
126 RepositoryGroup repositoryGroup = provider.createRepositoryGroup(config);
127 repositoryGroup.registerEventHandler( RepositoryEvent.ANY, repositoryRegistry);
128 updateRepositoryReferences(provider, repositoryGroup, config);
129 return repositoryGroup;
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()));
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.
145 * @param repositoryGroup the new repository group.
146 * @throws RepositoryException if the new repository group could not be saved to the configuration.
148 public RepositoryGroup putRepositoryGroup( RepositoryGroup repositoryGroup ) throws RepositoryException {
149 final String id = repositoryGroup.getId();
150 RepositoryGroup originRepoGroup = repositoryGroups.put(id, repositoryGroup);
152 if (originRepoGroup != null && originRepoGroup != repositoryGroup) {
153 originRepoGroup.close();
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);
163 configuration.addRepositoryGroup(newCfg);
164 repositoryRegistry.saveConfiguration(configuration);
165 return repositoryGroup;
166 } catch (Exception e) {
168 if (originRepoGroup != null) {
169 repositoryGroups.put(id, originRepoGroup);
171 repositoryGroups.remove(id);
173 log.error("Exception during configuration update {}", e.getMessage(), e);
174 throw new RepositoryException("Could not save the configuration" + (e.getMessage() == null ? "" : ": " + e.getMessage()));
179 * Adds a new repository group or updates the repository with the same id, if it exists already.
180 * The configuration is saved immediately.
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.
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);
194 repositoryRegistry.saveConfiguration(configuration);
195 } catch ( IndeterminateConfigurationException | RegistryException e) {
196 if (oldCfg != null) {
197 repositoryRegistry.getProvider(repositoryType).updateRepositoryGroupInstance((EditableRepositoryGroup) repo, oldCfg);
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());
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);
215 throw new RepositoryException("The repository is not editable " + id);
218 repo = repositoryRegistry.getProvider(repoType).createRepositoryGroup(repositoryGroupConfiguration);
219 repositoryGroups.put(id, repo);
221 updateRepositoryReferences(repositoryRegistry.getProvider(repoType), repo, repositoryGroupConfiguration);
222 replaceOrAddRepositoryConfig(repositoryGroupConfiguration, configuration);
226 private void setRepositoryGroupDefaults(RepositoryGroupConfiguration repositoryGroupConfiguration) {
227 if ( StringUtils.isEmpty(repositoryGroupConfiguration.getMergedIndexPath())) {
228 repositoryGroupConfiguration.setMergedIndexPath(DEFAULT_INDEX_PATH);
230 if (repositoryGroupConfiguration.getMergedIndexTtl() <= 0) {
231 repositoryGroupConfiguration.setMergedIndexTtl(300);
233 if (StringUtils.isEmpty(repositoryGroupConfiguration.getCronExpression())) {
234 repositoryGroupConfiguration.setCronExpression("0 0 03 ? * MON");
238 private void replaceOrAddRepositoryConfig(RepositoryGroupConfiguration repositoryGroupConfiguration, Configuration configuration) {
239 RepositoryGroupConfiguration oldCfg = configuration.findRepositoryGroupById(repositoryGroupConfiguration.getId());
240 if (oldCfg != null) {
241 configuration.removeRepositoryGroup(oldCfg);
243 configuration.addRepositoryGroup(repositoryGroupConfiguration);
246 public void removeRepositoryFromGroups( ManagedRepository repo) {
248 repositoryGroups.values().stream().filter(repoGroup -> repoGroup instanceof EditableRepository ).
249 map(repoGroup -> (EditableRepositoryGroup) repoGroup).forEach(repoGroup -> repoGroup.removeRepository(repo));
254 * Removes a repository group from the registry and configuration, if it exists.
255 * The change is saved to the configuration immediately.
257 * @param id the id of the repository group to remove
258 * @throws RepositoryException if a error occurs during configuration save
260 public void removeRepositoryGroup( final String id ) throws RepositoryException {
261 RepositoryGroup repo = getRepositoryGroup(id);
264 repo = repositoryGroups.remove(id);
267 Configuration configuration = this.configurationHandler.getBaseConfiguration();
268 RepositoryGroupConfiguration cfg = configuration.findRepositoryGroupById(id);
270 configuration.removeRepositoryGroup(cfg);
272 this.configurationHandler.save(configuration, ConfigurationHandler.REGISTRY_EVENT_TAG );
275 } catch (RegistryException | IndeterminateConfigurationException e) {
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());
284 public void removeRepositoryGroup( String id, Configuration configuration ) throws RepositoryException {
285 RepositoryGroup repo = repositoryGroups.get(id);
287 repo = repositoryGroups.remove(id);
290 RepositoryGroupConfiguration cfg = configuration.findRepositoryGroupById(id);
292 configuration.removeRepositoryGroup(cfg);
299 public RepositoryGroup getRepositoryGroup( String groupId ) {
300 return repositoryGroups.get(groupId);
303 public Collection<RepositoryGroup> getRepositoryGroups() {
304 return repositoryGroups.values( );
307 public boolean hasRepositoryGroup(String id) {
308 return repositoryGroups.containsKey( id );
312 private void destroy() {
316 public void close() {
317 for (RepositoryGroup group : repositoryGroups.values()) {
321 } catch (Throwable e) {
322 log.error( "Could not close repository group {}: {}", group.getId( ), e.getMessage( ) );
325 this.repositoryGroups.clear();