1 package org.apache.archiva.repository.maven;
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.event.Event;
26 import org.apache.archiva.repository.features.ArtifactCleanupFeature;
27 import org.apache.archiva.repository.features.IndexCreationFeature;
28 import org.apache.archiva.repository.features.RemoteIndexFeature;
29 import org.apache.archiva.repository.features.StagingRepositoryFeature;
30 import org.apache.archiva.repository.base.BasicManagedRepository;
31 import org.apache.archiva.repository.base.PasswordCredentials;
32 import org.apache.archiva.repository.storage.fs.FilesystemStorage;
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.inject.Inject;
39 import java.io.IOException;
41 import java.net.URISyntaxException;
42 import java.nio.file.Files;
43 import java.nio.file.Path;
44 import java.nio.file.Paths;
45 import java.time.Duration;
46 import java.time.Period;
47 import java.time.temporal.ChronoUnit;
48 import java.util.HashSet;
50 import java.util.stream.Collectors;
52 import static org.apache.archiva.indexer.ArchivaIndexManager.DEFAULT_INDEX_PATH;
53 import static org.apache.archiva.indexer.ArchivaIndexManager.DEFAULT_PACKED_INDEX_PATH;
56 * Provider for the maven2 repository implementations
58 @Service("mavenRepositoryProvider")
59 public class MavenRepositoryProvider implements RepositoryProvider {
63 private ArchivaConfiguration archivaConfiguration;
66 private FileLockManager fileLockManager;
68 private static final Logger log = LoggerFactory.getLogger(MavenRepositoryProvider.class);
70 static final Set<RepositoryType> TYPES = new HashSet<>();
73 TYPES.add(RepositoryType.MAVEN);
77 public Set<RepositoryType> provides() {
82 public MavenManagedRepository createManagedInstance(String id, String name) {
83 return createManagedInstance(id, name, archivaConfiguration.getRemoteRepositoryBaseDir());
86 public MavenManagedRepository createManagedInstance(String id, String name, Path baseDir) {
87 FilesystemStorage storage = null;
89 storage = new FilesystemStorage(baseDir.resolve(id), fileLockManager);
90 } catch (IOException e) {
91 log.error("Could not initialize fileystem for repository {}", id);
92 throw new RuntimeException(e);
94 return new MavenManagedRepository(id, name, storage);
98 public MavenRemoteRepository createRemoteInstance(String id, String name) {
99 return createRemoteInstance(id, name, archivaConfiguration.getRemoteRepositoryBaseDir());
102 public MavenRemoteRepository createRemoteInstance(String id, String name, Path baseDir) {
103 FilesystemStorage storage = null;
105 storage = new FilesystemStorage(baseDir.resolve(id), fileLockManager);
106 } catch (IOException e) {
107 log.error("Could not initialize fileystem for repository {}", id);
108 throw new RuntimeException(e);
110 return new MavenRemoteRepository(id, name, storage);
114 public EditableRepositoryGroup createRepositoryGroup(String id, String name) {
115 return createRepositoryGroup(id, name, archivaConfiguration.getRepositoryBaseDir());
118 public MavenRepositoryGroup createRepositoryGroup(String id, String name, Path baseDir) {
119 FilesystemStorage storage = null;
121 storage = new FilesystemStorage(baseDir.resolve(id), fileLockManager);
122 } catch (IOException e) {
123 log.error("Could not initialize fileystem for repository {}", id);
124 throw new RuntimeException(e);
126 return new MavenRepositoryGroup(id, name, storage);
129 private URI getURIFromString(String uriStr) throws RepositoryException {
132 if (StringUtils.isEmpty(uriStr)) {
135 if (uriStr.startsWith("/")) {
136 // only absolute paths are prepended with file scheme
137 uri = new URI("file://" + uriStr);
138 } else if (uriStr.contains(":\\")) {
139 //windows absolute path drive
140 uri = new URI("file:///" + uriStr.replaceAll("\\\\", "/"));
142 uri = new URI(uriStr);
144 if (uri.getScheme() != null && !"file".equals(uri.getScheme())) {
145 log.error("Bad URI scheme found: {}, URI={}", uri.getScheme(), uri);
146 throw new RepositoryException("The uri " + uriStr + " is not valid. Only file:// URI is allowed for maven.");
148 } catch (URISyntaxException e) {
149 String newCfg = "file://" + uriStr;
151 uri = new URI(newCfg);
152 } catch (URISyntaxException e1) {
153 log.error("Could not create URI from {} -> ", uriStr, newCfg);
154 throw new RepositoryException("The config entry " + uriStr + " cannot be converted to URI.");
157 log.debug("Setting location uri: {}", uri);
162 public ManagedRepository createManagedInstance(ManagedRepositoryConfiguration cfg) throws RepositoryException {
163 MavenManagedRepository repo = createManagedInstance(cfg.getId(), cfg.getName(), Paths.get(cfg.getLocation()).getParent());
164 updateManagedInstance(repo, cfg);
169 public void updateManagedInstance(EditableManagedRepository repo, ManagedRepositoryConfiguration cfg) throws RepositoryException {
171 repo.setLocation(getURIFromString(cfg.getLocation()));
172 } catch (UnsupportedURIException e) {
173 throw new RepositoryException("The location entry is not a valid uri: " + cfg.getLocation());
175 setBaseConfig(repo, cfg);
176 Path repoDir = repo.getAsset("").getFilePath();
177 if (!Files.exists(repoDir)) {
178 log.debug("Creating repo directory {}", repoDir);
180 Files.createDirectories(repoDir);
181 } catch (IOException e) {
182 log.error("Could not create directory {} for repository {}", repoDir, repo.getId(), e);
183 throw new RepositoryException("Could not create directory for repository " + repoDir);
186 repo.setSchedulingDefinition(cfg.getRefreshCronExpression());
187 repo.setBlocksRedeployment(cfg.isBlockRedeployments());
188 repo.setScanned(cfg.isScanned());
189 if (cfg.isReleases()) {
190 repo.addActiveReleaseScheme(ReleaseScheme.RELEASE);
192 repo.removeActiveReleaseScheme(ReleaseScheme.RELEASE);
194 if (cfg.isSnapshots()) {
195 repo.addActiveReleaseScheme(ReleaseScheme.SNAPSHOT);
197 repo.removeActiveReleaseScheme(ReleaseScheme.SNAPSHOT);
200 StagingRepositoryFeature stagingRepositoryFeature = repo.getFeature(StagingRepositoryFeature.class).get();
201 stagingRepositoryFeature.setStageRepoNeeded(cfg.isStageRepoNeeded());
203 IndexCreationFeature indexCreationFeature = repo.getFeature(IndexCreationFeature.class).get();
204 indexCreationFeature.setSkipPackedIndexCreation(cfg.isSkipPackedIndexCreation());
205 String indexDir = StringUtils.isEmpty( cfg.getIndexDir() ) ? DEFAULT_INDEX_PATH : cfg.getIndexDir();
206 String packedIndexDir = StringUtils.isEmpty( cfg.getPackedIndexDir() ) ? DEFAULT_PACKED_INDEX_PATH : cfg.getPackedIndexDir();
207 indexCreationFeature.setIndexPath(getURIFromString(indexDir));
208 indexCreationFeature.setPackedIndexPath(getURIFromString(packedIndexDir));
210 ArtifactCleanupFeature artifactCleanupFeature = repo.getFeature(ArtifactCleanupFeature.class).get();
212 artifactCleanupFeature.setDeleteReleasedSnapshots(cfg.isDeleteReleasedSnapshots());
213 artifactCleanupFeature.setRetentionCount(cfg.getRetentionCount());
214 artifactCleanupFeature.setRetentionPeriod(Period.ofDays(cfg.getRetentionPeriod()));
219 public ManagedRepository createStagingInstance(ManagedRepositoryConfiguration baseConfiguration) throws RepositoryException {
220 log.debug("Creating staging instance for {}", baseConfiguration.getId());
221 return createManagedInstance(getStageRepoConfig(baseConfiguration));
226 public RemoteRepository createRemoteInstance(RemoteRepositoryConfiguration cfg) throws RepositoryException {
227 MavenRemoteRepository repo = createRemoteInstance(cfg.getId(), cfg.getName(), archivaConfiguration.getRemoteRepositoryBaseDir());
228 updateRemoteInstance(repo, cfg);
232 private String convertUriToPath(URI uri) {
233 if (uri.getScheme() == null) {
234 return uri.getPath();
235 } else if ("file".equals(uri.getScheme())) {
236 return Paths.get(uri).toString();
238 return uri.toString();
243 public void updateRemoteInstance(EditableRemoteRepository repo, RemoteRepositoryConfiguration cfg) throws RepositoryException {
244 setBaseConfig(repo, cfg);
245 repo.setCheckPath(cfg.getCheckPath());
246 repo.setSchedulingDefinition(cfg.getRefreshCronExpression());
248 repo.setLocation(new URI(cfg.getUrl()));
249 } catch (UnsupportedURIException | URISyntaxException e) {
250 log.error("Could not set remote url " + cfg.getUrl());
251 throw new RepositoryException("The url config is not a valid uri: " + cfg.getUrl());
253 repo.setTimeout(Duration.ofSeconds(cfg.getTimeout()));
254 RemoteIndexFeature remoteIndexFeature = repo.getFeature(RemoteIndexFeature.class).get();
255 remoteIndexFeature.setDownloadRemoteIndex(cfg.isDownloadRemoteIndex());
256 remoteIndexFeature.setDownloadRemoteIndexOnStartup(cfg.isDownloadRemoteIndexOnStartup());
257 remoteIndexFeature.setDownloadTimeout(Duration.ofSeconds(cfg.getRemoteDownloadTimeout()));
258 remoteIndexFeature.setProxyId(cfg.getRemoteDownloadNetworkProxyId());
259 if (cfg.isDownloadRemoteIndex()) {
261 remoteIndexFeature.setIndexUri(new URI(cfg.getRemoteIndexUrl()));
262 } catch (URISyntaxException e) {
263 log.error("Could not set remote index url " + cfg.getRemoteIndexUrl());
264 remoteIndexFeature.setDownloadRemoteIndex(false);
265 remoteIndexFeature.setDownloadRemoteIndexOnStartup(false);
268 for ( Object key : cfg.getExtraHeaders().keySet() ) {
269 repo.addExtraHeader( key.toString(), cfg.getExtraHeaders().get(key).toString() );
271 for ( Object key : cfg.getExtraParameters().keySet() ) {
272 repo.addExtraParameter( key.toString(), cfg.getExtraParameters().get(key).toString() );
274 PasswordCredentials credentials = new PasswordCredentials("", new char[0]);
275 if (cfg.getPassword() != null && cfg.getUsername() != null) {
276 credentials.setPassword(cfg.getPassword().toCharArray());
277 credentials.setUsername(cfg.getUsername());
278 repo.setCredentials(credentials);
280 credentials.setPassword(new char[0]);
282 IndexCreationFeature indexCreationFeature = repo.getFeature(IndexCreationFeature.class).get();
283 if (cfg.getIndexDir() != null) {
284 indexCreationFeature.setIndexPath(getURIFromString(cfg.getIndexDir()));
286 if (cfg.getPackedIndexDir() != null) {
287 indexCreationFeature.setPackedIndexPath(getURIFromString(cfg.getPackedIndexDir()));
289 log.debug("Updated remote instance {}", repo);
293 public RepositoryGroup createRepositoryGroup(RepositoryGroupConfiguration configuration) throws RepositoryException {
294 Path repositoryGroupBase = getArchivaConfiguration().getRepositoryGroupBaseDir();
295 MavenRepositoryGroup newGrp = createRepositoryGroup(configuration.getId(), configuration.getName(),
296 repositoryGroupBase);
297 updateRepositoryGroupInstance(newGrp, configuration);
302 public void updateRepositoryGroupInstance(EditableRepositoryGroup repositoryGroup, RepositoryGroupConfiguration configuration) throws RepositoryException {
303 repositoryGroup.setName(repositoryGroup.getPrimaryLocale(), configuration.getName());
304 repositoryGroup.setMergedIndexTTL(configuration.getMergedIndexTtl());
305 repositoryGroup.setSchedulingDefinition(configuration.getCronExpression());
306 if (repositoryGroup.supportsFeature( IndexCreationFeature.class )) {
307 IndexCreationFeature indexCreationFeature = repositoryGroup.getFeature( IndexCreationFeature.class ).get();
308 indexCreationFeature.setIndexPath( getURIFromString(configuration.getMergedIndexPath()) );
309 Path localPath = Paths.get(configuration.getMergedIndexPath());
310 Path repoGroupPath = repositoryGroup.getAsset("").getFilePath().toAbsolutePath();
311 if (localPath.isAbsolute() && !localPath.startsWith(repoGroupPath)) {
313 FilesystemStorage storage = new FilesystemStorage(localPath.getParent(), fileLockManager);
314 indexCreationFeature.setLocalIndexPath(storage.getAsset(localPath.getFileName().toString()));
315 } catch (IOException e) {
316 throw new RepositoryException("Could not initialize storage for index path "+localPath);
318 } else if (localPath.isAbsolute()) {
319 indexCreationFeature.setLocalIndexPath(repositoryGroup.getAsset(repoGroupPath.relativize(localPath).toString()));
322 indexCreationFeature.setLocalIndexPath(repositoryGroup.getAsset(localPath.toString()));
325 // References to other repositories are set filled by the registry
329 public RemoteRepositoryConfiguration getRemoteConfiguration(RemoteRepository remoteRepository) throws RepositoryException {
330 if (!(remoteRepository instanceof MavenRemoteRepository)) {
331 log.error("Wrong remote repository type " + remoteRepository.getClass().getName());
332 throw new RepositoryException("The given repository type cannot be handled by the maven provider: " + remoteRepository.getClass().getName());
334 RemoteRepositoryConfiguration cfg = new RemoteRepositoryConfiguration();
335 cfg.setType(remoteRepository.getType().toString());
336 cfg.setId(remoteRepository.getId());
337 cfg.setName(remoteRepository.getName());
338 cfg.setDescription(remoteRepository.getDescription());
339 cfg.setUrl(remoteRepository.getLocation().toString());
340 cfg.setTimeout((int) remoteRepository.getTimeout().toMillis() / 1000);
341 cfg.setCheckPath(remoteRepository.getCheckPath());
342 RepositoryCredentials creds = remoteRepository.getLoginCredentials();
344 if (creds instanceof PasswordCredentials) {
345 PasswordCredentials pCreds = (PasswordCredentials) creds;
346 cfg.setPassword(new String(pCreds.getPassword()));
347 cfg.setUsername(pCreds.getUsername());
350 cfg.setLayout(remoteRepository.getLayout());
351 cfg.setExtraParameters(remoteRepository.getExtraParameters());
352 cfg.setExtraHeaders(remoteRepository.getExtraHeaders());
353 cfg.setRefreshCronExpression(remoteRepository.getSchedulingDefinition());
355 IndexCreationFeature indexCreationFeature = remoteRepository.getFeature(IndexCreationFeature.class).get();
356 cfg.setIndexDir(convertUriToPath(indexCreationFeature.getIndexPath()));
357 cfg.setPackedIndexDir(convertUriToPath(indexCreationFeature.getPackedIndexPath()));
359 RemoteIndexFeature remoteIndexFeature = remoteRepository.getFeature(RemoteIndexFeature.class).get();
360 if (remoteIndexFeature.getIndexUri() != null) {
361 cfg.setRemoteIndexUrl(remoteIndexFeature.getIndexUri().toString());
363 cfg.setRemoteDownloadTimeout((int) remoteIndexFeature.getDownloadTimeout().get(ChronoUnit.SECONDS));
364 cfg.setDownloadRemoteIndexOnStartup(remoteIndexFeature.isDownloadRemoteIndexOnStartup());
365 cfg.setDownloadRemoteIndex(remoteIndexFeature.isDownloadRemoteIndex());
366 cfg.setRemoteDownloadNetworkProxyId(remoteIndexFeature.getProxyId());
367 if (!StringUtils.isEmpty(remoteIndexFeature.getProxyId())) {
368 cfg.setRemoteDownloadNetworkProxyId(remoteIndexFeature.getProxyId());
370 cfg.setRemoteDownloadNetworkProxyId("");
381 public ManagedRepositoryConfiguration getManagedConfiguration(ManagedRepository managedRepository) throws RepositoryException {
382 if (!(managedRepository instanceof MavenManagedRepository || managedRepository instanceof BasicManagedRepository )) {
383 log.error("Wrong remote repository type " + managedRepository.getClass().getName());
384 throw new RepositoryException("The given repository type cannot be handled by the maven provider: " + managedRepository.getClass().getName());
386 ManagedRepositoryConfiguration cfg = new ManagedRepositoryConfiguration();
387 cfg.setType(managedRepository.getType().toString());
388 cfg.setId(managedRepository.getId());
389 cfg.setName(managedRepository.getName());
390 cfg.setDescription(managedRepository.getDescription());
391 cfg.setLocation(convertUriToPath(managedRepository.getLocation()));
392 cfg.setLayout(managedRepository.getLayout());
393 cfg.setRefreshCronExpression(managedRepository.getSchedulingDefinition());
394 cfg.setScanned(managedRepository.isScanned());
395 cfg.setBlockRedeployments(managedRepository.blocksRedeployments());
396 StagingRepositoryFeature stagingRepositoryFeature = managedRepository.getFeature(StagingRepositoryFeature.class).get();
397 cfg.setStageRepoNeeded(stagingRepositoryFeature.isStageRepoNeeded());
398 IndexCreationFeature indexCreationFeature = managedRepository.getFeature(IndexCreationFeature.class).get();
399 cfg.setIndexDir(convertUriToPath(indexCreationFeature.getIndexPath()));
400 cfg.setPackedIndexDir(convertUriToPath(indexCreationFeature.getPackedIndexPath()));
401 cfg.setSkipPackedIndexCreation(indexCreationFeature.isSkipPackedIndexCreation());
403 ArtifactCleanupFeature artifactCleanupFeature = managedRepository.getFeature(ArtifactCleanupFeature.class).get();
404 cfg.setRetentionCount(artifactCleanupFeature.getRetentionCount());
405 cfg.setRetentionPeriod(artifactCleanupFeature.getRetentionPeriod().getDays());
406 cfg.setDeleteReleasedSnapshots(artifactCleanupFeature.isDeleteReleasedSnapshots());
408 if (managedRepository.getActiveReleaseSchemes().contains(ReleaseScheme.RELEASE)) {
409 cfg.setReleases(true);
411 cfg.setReleases(false);
413 if (managedRepository.getActiveReleaseSchemes().contains(ReleaseScheme.SNAPSHOT)) {
414 cfg.setSnapshots(true);
416 cfg.setSnapshots(false);
423 public RepositoryGroupConfiguration getRepositoryGroupConfiguration(RepositoryGroup repositoryGroup) throws RepositoryException {
424 if (repositoryGroup.getType() != RepositoryType.MAVEN) {
425 throw new RepositoryException("The given repository group is not of MAVEN type");
427 RepositoryGroupConfiguration cfg = new RepositoryGroupConfiguration();
428 cfg.setId(repositoryGroup.getId());
429 cfg.setName(repositoryGroup.getName());
430 if (repositoryGroup.supportsFeature( IndexCreationFeature.class ))
432 IndexCreationFeature indexCreationFeature = repositoryGroup.getFeature( IndexCreationFeature.class ).get();
434 cfg.setMergedIndexPath( indexCreationFeature.getIndexPath().toString() );
436 cfg.setMergedIndexTtl(repositoryGroup.getMergedIndexTTL());
437 cfg.setRepositories(repositoryGroup.getRepositories().stream().map(r -> r.getId()).collect(Collectors.toList()));
438 cfg.setCronExpression(repositoryGroup.getSchedulingDefinition());
442 private ManagedRepositoryConfiguration getStageRepoConfig(ManagedRepositoryConfiguration repository) {
443 ManagedRepositoryConfiguration stagingRepository = new ManagedRepositoryConfiguration();
444 stagingRepository.setId(repository.getId() + StagingRepositoryFeature.STAGING_REPO_POSTFIX);
445 stagingRepository.setLayout(repository.getLayout());
446 stagingRepository.setName(repository.getName() + StagingRepositoryFeature.STAGING_REPO_POSTFIX);
447 stagingRepository.setBlockRedeployments(repository.isBlockRedeployments());
448 stagingRepository.setRetentionPeriod(repository.getRetentionPeriod());
449 stagingRepository.setDeleteReleasedSnapshots(repository.isDeleteReleasedSnapshots());
450 stagingRepository.setStageRepoNeeded(false);
452 String path = repository.getLocation();
453 int lastIndex = path.replace('\\', '/').lastIndexOf('/');
454 stagingRepository.setLocation(path.substring(0, lastIndex) + "/" + stagingRepository.getId());
456 if (StringUtils.isNotBlank(repository.getIndexDir())) {
457 Path indexDir = null;
459 indexDir = Paths.get(new URI(repository.getIndexDir().startsWith("file://") ? repository.getIndexDir() : "file://" + repository.getIndexDir()));
460 if (indexDir.isAbsolute()) {
461 Path newDir = indexDir.getParent().resolve(indexDir.getFileName() + StagingRepositoryFeature.STAGING_REPO_POSTFIX);
462 log.debug("Changing index directory {} -> {}", indexDir, newDir);
463 stagingRepository.setIndexDir(newDir.toString());
465 log.debug("Keeping index directory {}", repository.getIndexDir());
466 stagingRepository.setIndexDir(repository.getIndexDir());
468 } catch (URISyntaxException e) {
469 log.error("Could not parse index path as uri {}", repository.getIndexDir());
470 stagingRepository.setIndexDir("");
472 // in case of absolute dir do not use the same
474 if (StringUtils.isNotBlank(repository.getPackedIndexDir())) {
475 Path packedIndexDir = null;
476 packedIndexDir = Paths.get(repository.getPackedIndexDir());
477 if (packedIndexDir.isAbsolute()) {
478 Path newDir = packedIndexDir.getParent().resolve(packedIndexDir.getFileName() + StagingRepositoryFeature.STAGING_REPO_POSTFIX);
479 log.debug("Changing index directory {} -> {}", packedIndexDir, newDir);
480 stagingRepository.setPackedIndexDir(newDir.toString());
482 log.debug("Keeping index directory {}", repository.getPackedIndexDir());
483 stagingRepository.setPackedIndexDir(repository.getPackedIndexDir());
485 // in case of absolute dir do not use the same
487 stagingRepository.setRefreshCronExpression(repository.getRefreshCronExpression());
488 stagingRepository.setReleases(repository.isReleases());
489 stagingRepository.setRetentionCount(repository.getRetentionCount());
490 stagingRepository.setScanned(repository.isScanned());
491 stagingRepository.setSnapshots(repository.isSnapshots());
492 stagingRepository.setSkipPackedIndexCreation(repository.isSkipPackedIndexCreation());
493 // do not duplicate description
494 //stagingRepository.getDescription("")
495 return stagingRepository;
498 private void setBaseConfig(EditableRepository repo, AbstractRepositoryConfiguration cfg) throws RepositoryException {
500 URI baseUri = archivaConfiguration.getRepositoryBaseDir().toUri();
501 repo.setBaseUri(baseUri);
503 repo.setName(repo.getPrimaryLocale(), cfg.getName());
504 repo.setDescription(repo.getPrimaryLocale(), cfg.getDescription());
505 repo.setLayout(cfg.getLayout());
508 public ArchivaConfiguration getArchivaConfiguration() {
509 return archivaConfiguration;
512 public void setArchivaConfiguration(ArchivaConfiguration archivaConfiguration) {
513 this.archivaConfiguration = archivaConfiguration;
517 public void handle(Event event) {