1 package org.apache.archiva.repository.maven2;
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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
22 import org.apache.archiva.common.filelock.FileLockManager;
23 import org.apache.archiva.configuration.*;
24 import org.apache.archiva.repository.*;
25 import org.apache.archiva.repository.content.FilesystemAsset;
26 import org.apache.archiva.repository.content.FilesystemStorage;
27 import org.apache.archiva.repository.features.ArtifactCleanupFeature;
28 import org.apache.archiva.repository.features.IndexCreationFeature;
29 import org.apache.archiva.repository.features.RemoteIndexFeature;
30 import org.apache.archiva.repository.features.StagingRepositoryFeature;
31 import org.apache.commons.lang.StringUtils;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34 import org.springframework.stereotype.Service;
36 import javax.inject.Inject;
37 import java.io.IOException;
39 import java.net.URISyntaxException;
40 import java.nio.file.Files;
41 import java.nio.file.Path;
42 import java.nio.file.Paths;
43 import java.time.Duration;
44 import java.time.Period;
45 import java.time.temporal.ChronoUnit;
46 import java.util.HashSet;
48 import java.util.stream.Collectors;
50 import static org.apache.archiva.indexer.ArchivaIndexManager.DEFAULT_INDEX_PATH;
51 import static org.apache.archiva.indexer.ArchivaIndexManager.DEFAULT_PACKED_INDEX_PATH;
54 * Provider for the maven2 repository implementations
56 @Service("mavenRepositoryProvider")
57 public class MavenRepositoryProvider implements RepositoryProvider {
61 private ArchivaConfiguration archivaConfiguration;
64 private FileLockManager fileLockManager;
66 private static final Logger log = LoggerFactory.getLogger(MavenRepositoryProvider.class);
68 static final Set<RepositoryType> TYPES = new HashSet<>();
71 TYPES.add(RepositoryType.MAVEN);
75 public Set<RepositoryType> provides() {
80 public MavenManagedRepository createManagedInstance(String id, String name) {
81 return createManagedInstance(id, name, archivaConfiguration.getRemoteRepositoryBaseDir());
84 public MavenManagedRepository createManagedInstance(String id, String name, Path baseDir) {
85 FilesystemStorage storage = null;
87 storage = new FilesystemStorage(baseDir.resolve(id), fileLockManager);
88 } catch (IOException e) {
89 log.error("Could not initialize fileystem for repository {}", id);
90 throw new RuntimeException(e);
92 return new MavenManagedRepository(id, name, storage);
96 public MavenRemoteRepository createRemoteInstance(String id, String name) {
97 return createRemoteInstance(id, name, archivaConfiguration.getRemoteRepositoryBaseDir());
100 public MavenRemoteRepository createRemoteInstance(String id, String name, Path baseDir) {
101 FilesystemStorage storage = null;
103 storage = new FilesystemStorage(baseDir.resolve(id), fileLockManager);
104 } catch (IOException e) {
105 log.error("Could not initialize fileystem for repository {}", id);
106 throw new RuntimeException(e);
108 return new MavenRemoteRepository(id, name, storage);
112 public EditableRepositoryGroup createRepositoryGroup(String id, String name) {
113 return createRepositoryGroup(id, name, archivaConfiguration.getRepositoryBaseDir());
116 public MavenRepositoryGroup createRepositoryGroup(String id, String name, Path baseDir) {
117 FilesystemStorage storage = null;
119 storage = new FilesystemStorage(baseDir.resolve(id), fileLockManager);
120 } catch (IOException e) {
121 log.error("Could not initialize fileystem for repository {}", id);
122 throw new RuntimeException(e);
124 return new MavenRepositoryGroup(id, name, storage);
127 private URI getURIFromString(String uriStr) throws RepositoryException {
130 if (StringUtils.isEmpty(uriStr)) {
133 if (uriStr.startsWith("/")) {
134 // only absolute paths are prepended with file scheme
135 uri = new URI("file://" + uriStr);
137 uri = new URI(uriStr);
139 if (uri.getScheme() != null && !"file".equals(uri.getScheme())) {
140 log.error("Bad URI scheme found: {}, URI={}", uri.getScheme(), uri);
141 throw new RepositoryException("The uri " + uriStr + " is not valid. Only file:// URI is allowed for maven.");
143 } catch (URISyntaxException e) {
144 String newCfg = "file://" + uriStr;
146 uri = new URI(newCfg);
147 } catch (URISyntaxException e1) {
148 log.error("Could not create URI from {} -> ", uriStr, newCfg);
149 throw new RepositoryException("The config entry " + uriStr + " cannot be converted to URI.");
152 log.debug("Setting location uri: {}", uri);
157 public ManagedRepository createManagedInstance(ManagedRepositoryConfiguration cfg) throws RepositoryException {
158 MavenManagedRepository repo = createManagedInstance(cfg.getId(), cfg.getName(), Paths.get(cfg.getLocation()).getParent());
159 updateManagedInstance(repo, cfg);
164 public void updateManagedInstance(EditableManagedRepository repo, ManagedRepositoryConfiguration cfg) throws RepositoryException {
166 repo.setLocation(getURIFromString(cfg.getLocation()));
167 } catch (UnsupportedURIException e) {
168 throw new RepositoryException("The location entry is not a valid uri: " + cfg.getLocation());
170 setBaseConfig(repo, cfg);
171 Path repoDir = repo.getAsset("").getFilePath();
172 if (!Files.exists(repoDir)) {
173 log.debug("Creating repo directory {}", repoDir);
175 Files.createDirectories(repoDir);
176 } catch (IOException e) {
177 log.error("Could not create directory {} for repository {}", repoDir, repo.getId(), e);
178 throw new RepositoryException("Could not create directory for repository " + repoDir);
181 repo.setSchedulingDefinition(cfg.getRefreshCronExpression());
182 repo.setBlocksRedeployment(cfg.isBlockRedeployments());
183 repo.setScanned(cfg.isScanned());
184 if (cfg.isReleases()) {
185 repo.addActiveReleaseScheme(ReleaseScheme.RELEASE);
187 if (cfg.isSnapshots()) {
188 repo.addActiveReleaseScheme(ReleaseScheme.SNAPSHOT);
191 StagingRepositoryFeature stagingRepositoryFeature = repo.getFeature(StagingRepositoryFeature.class).get();
192 stagingRepositoryFeature.setStageRepoNeeded(cfg.isStageRepoNeeded());
194 IndexCreationFeature indexCreationFeature = repo.getFeature(IndexCreationFeature.class).get();
195 indexCreationFeature.setSkipPackedIndexCreation(cfg.isSkipPackedIndexCreation());
196 String indexDir = StringUtils.isEmpty( cfg.getIndexDir() ) ? DEFAULT_INDEX_PATH : cfg.getIndexDir();
197 String packedIndexDir = StringUtils.isEmpty( cfg.getPackedIndexDir() ) ? DEFAULT_PACKED_INDEX_PATH : cfg.getPackedIndexDir();
198 indexCreationFeature.setIndexPath(getURIFromString(indexDir));
199 indexCreationFeature.setPackedIndexPath(getURIFromString(packedIndexDir));
201 ArtifactCleanupFeature artifactCleanupFeature = repo.getFeature(ArtifactCleanupFeature.class).get();
203 artifactCleanupFeature.setDeleteReleasedSnapshots(cfg.isDeleteReleasedSnapshots());
204 artifactCleanupFeature.setRetentionCount(cfg.getRetentionCount());
205 artifactCleanupFeature.setRetentionPeriod(Period.ofDays(cfg.getRetentionPeriod()));
210 public ManagedRepository createStagingInstance(ManagedRepositoryConfiguration baseConfiguration) throws RepositoryException {
211 log.debug("Creating staging instance for {}", baseConfiguration.getId());
212 return createManagedInstance(getStageRepoConfig(baseConfiguration));
217 public RemoteRepository createRemoteInstance(RemoteRepositoryConfiguration cfg) throws RepositoryException {
218 MavenRemoteRepository repo = createRemoteInstance(cfg.getId(), cfg.getName(), archivaConfiguration.getRemoteRepositoryBaseDir());
219 updateRemoteInstance(repo, cfg);
223 private String convertUriToPath(URI uri) {
224 if (uri.getScheme() == null) {
225 return uri.getPath();
226 } else if ("file".equals(uri.getScheme())) {
227 return Paths.get(uri).toString();
229 return uri.toString();
234 public void updateRemoteInstance(EditableRemoteRepository repo, RemoteRepositoryConfiguration cfg) throws RepositoryException {
235 setBaseConfig(repo, cfg);
236 repo.setCheckPath(cfg.getCheckPath());
237 repo.setSchedulingDefinition(cfg.getRefreshCronExpression());
239 repo.setLocation(new URI(cfg.getUrl()));
240 } catch (UnsupportedURIException | URISyntaxException e) {
241 log.error("Could not set remote url " + cfg.getUrl());
242 throw new RepositoryException("The url config is not a valid uri: " + cfg.getUrl());
244 repo.setTimeout(Duration.ofSeconds(cfg.getTimeout()));
245 RemoteIndexFeature remoteIndexFeature = repo.getFeature(RemoteIndexFeature.class).get();
246 remoteIndexFeature.setDownloadRemoteIndex(cfg.isDownloadRemoteIndex());
247 remoteIndexFeature.setDownloadRemoteIndexOnStartup(cfg.isDownloadRemoteIndexOnStartup());
248 remoteIndexFeature.setDownloadTimeout(Duration.ofSeconds(cfg.getRemoteDownloadTimeout()));
249 remoteIndexFeature.setProxyId(cfg.getRemoteDownloadNetworkProxyId());
250 if (cfg.isDownloadRemoteIndex()) {
252 remoteIndexFeature.setIndexUri(new URI(cfg.getRemoteIndexUrl()));
253 } catch (URISyntaxException e) {
254 log.error("Could not set remote index url " + cfg.getRemoteIndexUrl());
255 remoteIndexFeature.setDownloadRemoteIndex(false);
256 remoteIndexFeature.setDownloadRemoteIndexOnStartup(false);
259 for ( Object key : cfg.getExtraHeaders().keySet() ) {
260 repo.addExtraHeader( key.toString(), cfg.getExtraHeaders().get(key).toString() );
262 for ( Object key : cfg.getExtraParameters().keySet() ) {
263 repo.addExtraParameter( key.toString(), cfg.getExtraParameters().get(key).toString() );
265 PasswordCredentials credentials = new PasswordCredentials("", new char[0]);
266 if (cfg.getPassword() != null && cfg.getUsername() != null) {
267 credentials.setPassword(cfg.getPassword().toCharArray());
268 credentials.setUsername(cfg.getUsername());
269 repo.setCredentials(credentials);
271 credentials.setPassword(new char[0]);
273 IndexCreationFeature indexCreationFeature = repo.getFeature(IndexCreationFeature.class).get();
274 if (cfg.getIndexDir() != null) {
275 indexCreationFeature.setIndexPath(getURIFromString(cfg.getIndexDir()));
277 if (cfg.getPackedIndexDir() != null) {
278 indexCreationFeature.setPackedIndexPath(getURIFromString(cfg.getPackedIndexDir()));
280 log.debug("Updated remote instance {}", repo);
284 public RepositoryGroup createRepositoryGroup(RepositoryGroupConfiguration configuration) throws RepositoryException {
285 Path repositoryGroupBase = getArchivaConfiguration().getRepositoryGroupBaseDir();
286 MavenRepositoryGroup newGrp = createRepositoryGroup(configuration.getId(), configuration.getName(),
287 repositoryGroupBase);
288 updateRepositoryGroupInstance(newGrp, configuration);
293 public void updateRepositoryGroupInstance(EditableRepositoryGroup repositoryGroup, RepositoryGroupConfiguration configuration) throws RepositoryException {
294 repositoryGroup.setName(repositoryGroup.getPrimaryLocale(), configuration.getName());
295 repositoryGroup.setMergedIndexTTL(configuration.getMergedIndexTtl());
296 repositoryGroup.setSchedulingDefinition(configuration.getCronExpression());
297 if (repositoryGroup.supportsFeature( IndexCreationFeature.class )) {
298 IndexCreationFeature indexCreationFeature = repositoryGroup.getFeature( IndexCreationFeature.class ).get();
299 indexCreationFeature.setIndexPath( getURIFromString(configuration.getMergedIndexPath()) );
300 Path localPath = Paths.get(configuration.getMergedIndexPath());
301 if (localPath.isAbsolute()) {
302 indexCreationFeature.setLocalIndexPath( new FilesystemAsset(localPath.getFileName().toString(), localPath) );
305 indexCreationFeature.setLocalIndexPath( new FilesystemAsset(localPath.toString(), archivaConfiguration.getRepositoryGroupBaseDir( ).resolve( localPath )));
308 // References to other repositories are set filled by the registry
312 public RemoteRepositoryConfiguration getRemoteConfiguration(RemoteRepository remoteRepository) throws RepositoryException {
313 if (!(remoteRepository instanceof MavenRemoteRepository)) {
314 log.error("Wrong remote repository type " + remoteRepository.getClass().getName());
315 throw new RepositoryException("The given repository type cannot be handled by the maven provider: " + remoteRepository.getClass().getName());
317 RemoteRepositoryConfiguration cfg = new RemoteRepositoryConfiguration();
318 cfg.setType(remoteRepository.getType().toString());
319 cfg.setId(remoteRepository.getId());
320 cfg.setName(remoteRepository.getName());
321 cfg.setDescription(remoteRepository.getDescription());
322 cfg.setUrl(remoteRepository.getLocation().toString());
323 cfg.setTimeout((int) remoteRepository.getTimeout().toMillis() / 1000);
324 cfg.setCheckPath(remoteRepository.getCheckPath());
325 RepositoryCredentials creds = remoteRepository.getLoginCredentials();
327 if (creds instanceof PasswordCredentials) {
328 PasswordCredentials pCreds = (PasswordCredentials) creds;
329 cfg.setPassword(new String(pCreds.getPassword()));
330 cfg.setUsername(pCreds.getUsername());
333 cfg.setLayout(remoteRepository.getLayout());
334 cfg.setExtraParameters(remoteRepository.getExtraParameters());
335 cfg.setExtraHeaders(remoteRepository.getExtraHeaders());
336 cfg.setRefreshCronExpression(remoteRepository.getSchedulingDefinition());
338 IndexCreationFeature indexCreationFeature = remoteRepository.getFeature(IndexCreationFeature.class).get();
339 cfg.setIndexDir(convertUriToPath(indexCreationFeature.getIndexPath()));
340 cfg.setPackedIndexDir(convertUriToPath(indexCreationFeature.getPackedIndexPath()));
342 RemoteIndexFeature remoteIndexFeature = remoteRepository.getFeature(RemoteIndexFeature.class).get();
343 cfg.setRemoteIndexUrl(remoteIndexFeature.getIndexUri().toString());
344 cfg.setRemoteDownloadTimeout((int) remoteIndexFeature.getDownloadTimeout().get(ChronoUnit.SECONDS));
345 cfg.setDownloadRemoteIndexOnStartup(remoteIndexFeature.isDownloadRemoteIndexOnStartup());
346 cfg.setDownloadRemoteIndex(remoteIndexFeature.isDownloadRemoteIndex());
347 cfg.setRemoteDownloadNetworkProxyId(remoteIndexFeature.getProxyId());
355 public ManagedRepositoryConfiguration getManagedConfiguration(ManagedRepository managedRepository) throws RepositoryException {
356 if (!(managedRepository instanceof MavenManagedRepository || managedRepository instanceof BasicManagedRepository)) {
357 log.error("Wrong remote repository type " + managedRepository.getClass().getName());
358 throw new RepositoryException("The given repository type cannot be handled by the maven provider: " + managedRepository.getClass().getName());
360 ManagedRepositoryConfiguration cfg = new ManagedRepositoryConfiguration();
361 cfg.setType(managedRepository.getType().toString());
362 cfg.setId(managedRepository.getId());
363 cfg.setName(managedRepository.getName());
364 cfg.setDescription(managedRepository.getDescription());
365 cfg.setLocation(convertUriToPath(managedRepository.getLocation()));
366 cfg.setLayout(managedRepository.getLayout());
367 cfg.setRefreshCronExpression(managedRepository.getSchedulingDefinition());
368 cfg.setScanned(managedRepository.isScanned());
369 cfg.setBlockRedeployments(managedRepository.blocksRedeployments());
370 StagingRepositoryFeature stagingRepositoryFeature = managedRepository.getFeature(StagingRepositoryFeature.class).get();
371 cfg.setStageRepoNeeded(stagingRepositoryFeature.isStageRepoNeeded());
372 IndexCreationFeature indexCreationFeature = managedRepository.getFeature(IndexCreationFeature.class).get();
373 cfg.setIndexDir(convertUriToPath(indexCreationFeature.getIndexPath()));
374 cfg.setPackedIndexDir(convertUriToPath(indexCreationFeature.getPackedIndexPath()));
375 cfg.setSkipPackedIndexCreation(indexCreationFeature.isSkipPackedIndexCreation());
377 ArtifactCleanupFeature artifactCleanupFeature = managedRepository.getFeature(ArtifactCleanupFeature.class).get();
378 cfg.setRetentionCount(artifactCleanupFeature.getRetentionCount());
379 cfg.setRetentionPeriod(artifactCleanupFeature.getRetentionPeriod().getDays());
380 cfg.setDeleteReleasedSnapshots(artifactCleanupFeature.isDeleteReleasedSnapshots());
382 if (managedRepository.getActiveReleaseSchemes().contains(ReleaseScheme.RELEASE)) {
383 cfg.setReleases(true);
385 cfg.setReleases(false);
387 if (managedRepository.getActiveReleaseSchemes().contains(ReleaseScheme.SNAPSHOT)) {
388 cfg.setSnapshots(true);
390 cfg.setSnapshots(false);
397 public RepositoryGroupConfiguration getRepositoryGroupConfiguration(RepositoryGroup repositoryGroup) throws RepositoryException {
398 if (repositoryGroup.getType() != RepositoryType.MAVEN) {
399 throw new RepositoryException("The given repository group is not of MAVEN type");
401 RepositoryGroupConfiguration cfg = new RepositoryGroupConfiguration();
402 cfg.setId(repositoryGroup.getId());
403 cfg.setName(repositoryGroup.getName());
404 if (repositoryGroup.supportsFeature( IndexCreationFeature.class ))
406 IndexCreationFeature indexCreationFeature = repositoryGroup.getFeature( IndexCreationFeature.class ).get();
408 cfg.setMergedIndexPath( indexCreationFeature.getIndexPath().toString() );
410 cfg.setMergedIndexTtl(repositoryGroup.getMergedIndexTTL());
411 cfg.setRepositories(repositoryGroup.getRepositories().stream().map(r -> r.getId()).collect(Collectors.toList()));
412 cfg.setCronExpression(repositoryGroup.getSchedulingDefinition());
416 private ManagedRepositoryConfiguration getStageRepoConfig(ManagedRepositoryConfiguration repository) {
417 ManagedRepositoryConfiguration stagingRepository = new ManagedRepositoryConfiguration();
418 stagingRepository.setId(repository.getId() + StagingRepositoryFeature.STAGING_REPO_POSTFIX);
419 stagingRepository.setLayout(repository.getLayout());
420 stagingRepository.setName(repository.getName() + StagingRepositoryFeature.STAGING_REPO_POSTFIX);
421 stagingRepository.setBlockRedeployments(repository.isBlockRedeployments());
422 stagingRepository.setRetentionPeriod(repository.getRetentionPeriod());
423 stagingRepository.setDeleteReleasedSnapshots(repository.isDeleteReleasedSnapshots());
424 stagingRepository.setStageRepoNeeded(false);
426 String path = repository.getLocation();
427 int lastIndex = path.replace('\\', '/').lastIndexOf('/');
428 stagingRepository.setLocation(path.substring(0, lastIndex) + "/" + stagingRepository.getId());
430 if (StringUtils.isNotBlank(repository.getIndexDir())) {
431 Path indexDir = null;
433 indexDir = Paths.get(new URI(repository.getIndexDir().startsWith("file://") ? repository.getIndexDir() : "file://" + repository.getIndexDir()));
434 if (indexDir.isAbsolute()) {
435 Path newDir = indexDir.getParent().resolve(indexDir.getFileName() + StagingRepositoryFeature.STAGING_REPO_POSTFIX);
436 log.debug("Changing index directory {} -> {}", indexDir, newDir);
437 stagingRepository.setIndexDir(newDir.toString());
439 log.debug("Keeping index directory {}", repository.getIndexDir());
440 stagingRepository.setIndexDir(repository.getIndexDir());
442 } catch (URISyntaxException e) {
443 log.error("Could not parse index path as uri {}", repository.getIndexDir());
444 stagingRepository.setIndexDir("");
446 // in case of absolute dir do not use the same
448 if (StringUtils.isNotBlank(repository.getPackedIndexDir())) {
449 Path packedIndexDir = null;
450 packedIndexDir = Paths.get(repository.getPackedIndexDir());
451 if (packedIndexDir.isAbsolute()) {
452 Path newDir = packedIndexDir.getParent().resolve(packedIndexDir.getFileName() + StagingRepositoryFeature.STAGING_REPO_POSTFIX);
453 log.debug("Changing index directory {} -> {}", packedIndexDir, newDir);
454 stagingRepository.setPackedIndexDir(newDir.toString());
456 log.debug("Keeping index directory {}", repository.getPackedIndexDir());
457 stagingRepository.setPackedIndexDir(repository.getPackedIndexDir());
459 // in case of absolute dir do not use the same
461 stagingRepository.setRefreshCronExpression(repository.getRefreshCronExpression());
462 stagingRepository.setReleases(repository.isReleases());
463 stagingRepository.setRetentionCount(repository.getRetentionCount());
464 stagingRepository.setScanned(repository.isScanned());
465 stagingRepository.setSnapshots(repository.isSnapshots());
466 stagingRepository.setSkipPackedIndexCreation(repository.isSkipPackedIndexCreation());
467 // do not duplicate description
468 //stagingRepository.getDescription("")
469 return stagingRepository;
472 private void setBaseConfig(EditableRepository repo, AbstractRepositoryConfiguration cfg) throws RepositoryException {
474 URI baseUri = archivaConfiguration.getRepositoryBaseDir().toUri();
475 repo.setBaseUri(baseUri);
477 repo.setName(repo.getPrimaryLocale(), cfg.getName());
478 repo.setDescription(repo.getPrimaryLocale(), cfg.getDescription());
479 repo.setLayout(cfg.getLayout());
482 public ArchivaConfiguration getArchivaConfiguration() {
483 return archivaConfiguration;
486 public void setArchivaConfiguration(ArchivaConfiguration archivaConfiguration) {
487 this.archivaConfiguration = archivaConfiguration;
491 public <T> void raise(RepositoryEvent<T> event) {