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.events.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.storage.FilesystemStorage;
31 import org.apache.commons.lang3.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 repo.removeActiveReleaseScheme(ReleaseScheme.RELEASE);
189 if (cfg.isSnapshots()) {
190 repo.addActiveReleaseScheme(ReleaseScheme.SNAPSHOT);
192 repo.removeActiveReleaseScheme(ReleaseScheme.SNAPSHOT);
195 StagingRepositoryFeature stagingRepositoryFeature = repo.getFeature(StagingRepositoryFeature.class).get();
196 stagingRepositoryFeature.setStageRepoNeeded(cfg.isStageRepoNeeded());
198 IndexCreationFeature indexCreationFeature = repo.getFeature(IndexCreationFeature.class).get();
199 indexCreationFeature.setSkipPackedIndexCreation(cfg.isSkipPackedIndexCreation());
200 String indexDir = StringUtils.isEmpty( cfg.getIndexDir() ) ? DEFAULT_INDEX_PATH : cfg.getIndexDir();
201 String packedIndexDir = StringUtils.isEmpty( cfg.getPackedIndexDir() ) ? DEFAULT_PACKED_INDEX_PATH : cfg.getPackedIndexDir();
202 indexCreationFeature.setIndexPath(getURIFromString(indexDir));
203 indexCreationFeature.setPackedIndexPath(getURIFromString(packedIndexDir));
205 ArtifactCleanupFeature artifactCleanupFeature = repo.getFeature(ArtifactCleanupFeature.class).get();
207 artifactCleanupFeature.setDeleteReleasedSnapshots(cfg.isDeleteReleasedSnapshots());
208 artifactCleanupFeature.setRetentionCount(cfg.getRetentionCount());
209 artifactCleanupFeature.setRetentionPeriod(Period.ofDays(cfg.getRetentionPeriod()));
214 public ManagedRepository createStagingInstance(ManagedRepositoryConfiguration baseConfiguration) throws RepositoryException {
215 log.debug("Creating staging instance for {}", baseConfiguration.getId());
216 return createManagedInstance(getStageRepoConfig(baseConfiguration));
221 public RemoteRepository createRemoteInstance(RemoteRepositoryConfiguration cfg) throws RepositoryException {
222 MavenRemoteRepository repo = createRemoteInstance(cfg.getId(), cfg.getName(), archivaConfiguration.getRemoteRepositoryBaseDir());
223 updateRemoteInstance(repo, cfg);
227 private String convertUriToPath(URI uri) {
228 if (uri.getScheme() == null) {
229 return uri.getPath();
230 } else if ("file".equals(uri.getScheme())) {
231 return Paths.get(uri).toString();
233 return uri.toString();
238 public void updateRemoteInstance(EditableRemoteRepository repo, RemoteRepositoryConfiguration cfg) throws RepositoryException {
239 setBaseConfig(repo, cfg);
240 repo.setCheckPath(cfg.getCheckPath());
241 repo.setSchedulingDefinition(cfg.getRefreshCronExpression());
243 repo.setLocation(new URI(cfg.getUrl()));
244 } catch (UnsupportedURIException | URISyntaxException e) {
245 log.error("Could not set remote url " + cfg.getUrl());
246 throw new RepositoryException("The url config is not a valid uri: " + cfg.getUrl());
248 repo.setTimeout(Duration.ofSeconds(cfg.getTimeout()));
249 RemoteIndexFeature remoteIndexFeature = repo.getFeature(RemoteIndexFeature.class).get();
250 remoteIndexFeature.setDownloadRemoteIndex(cfg.isDownloadRemoteIndex());
251 remoteIndexFeature.setDownloadRemoteIndexOnStartup(cfg.isDownloadRemoteIndexOnStartup());
252 remoteIndexFeature.setDownloadTimeout(Duration.ofSeconds(cfg.getRemoteDownloadTimeout()));
253 remoteIndexFeature.setProxyId(cfg.getRemoteDownloadNetworkProxyId());
254 if (cfg.isDownloadRemoteIndex()) {
256 remoteIndexFeature.setIndexUri(new URI(cfg.getRemoteIndexUrl()));
257 } catch (URISyntaxException e) {
258 log.error("Could not set remote index url " + cfg.getRemoteIndexUrl());
259 remoteIndexFeature.setDownloadRemoteIndex(false);
260 remoteIndexFeature.setDownloadRemoteIndexOnStartup(false);
263 for ( Object key : cfg.getExtraHeaders().keySet() ) {
264 repo.addExtraHeader( key.toString(), cfg.getExtraHeaders().get(key).toString() );
266 for ( Object key : cfg.getExtraParameters().keySet() ) {
267 repo.addExtraParameter( key.toString(), cfg.getExtraParameters().get(key).toString() );
269 PasswordCredentials credentials = new PasswordCredentials("", new char[0]);
270 if (cfg.getPassword() != null && cfg.getUsername() != null) {
271 credentials.setPassword(cfg.getPassword().toCharArray());
272 credentials.setUsername(cfg.getUsername());
273 repo.setCredentials(credentials);
275 credentials.setPassword(new char[0]);
277 IndexCreationFeature indexCreationFeature = repo.getFeature(IndexCreationFeature.class).get();
278 if (cfg.getIndexDir() != null) {
279 indexCreationFeature.setIndexPath(getURIFromString(cfg.getIndexDir()));
281 if (cfg.getPackedIndexDir() != null) {
282 indexCreationFeature.setPackedIndexPath(getURIFromString(cfg.getPackedIndexDir()));
284 log.debug("Updated remote instance {}", repo);
288 public RepositoryGroup createRepositoryGroup(RepositoryGroupConfiguration configuration) throws RepositoryException {
289 Path repositoryGroupBase = getArchivaConfiguration().getRepositoryGroupBaseDir();
290 MavenRepositoryGroup newGrp = createRepositoryGroup(configuration.getId(), configuration.getName(),
291 repositoryGroupBase);
292 updateRepositoryGroupInstance(newGrp, configuration);
297 public void updateRepositoryGroupInstance(EditableRepositoryGroup repositoryGroup, RepositoryGroupConfiguration configuration) throws RepositoryException {
298 repositoryGroup.setName(repositoryGroup.getPrimaryLocale(), configuration.getName());
299 repositoryGroup.setMergedIndexTTL(configuration.getMergedIndexTtl());
300 repositoryGroup.setSchedulingDefinition(configuration.getCronExpression());
301 if (repositoryGroup.supportsFeature( IndexCreationFeature.class )) {
302 IndexCreationFeature indexCreationFeature = repositoryGroup.getFeature( IndexCreationFeature.class ).get();
303 indexCreationFeature.setIndexPath( getURIFromString(configuration.getMergedIndexPath()) );
304 Path localPath = Paths.get(configuration.getMergedIndexPath());
305 Path repoGroupPath = repositoryGroup.getAsset("").getFilePath().toAbsolutePath();
306 if (localPath.isAbsolute() && !localPath.startsWith(repoGroupPath)) {
308 FilesystemStorage storage = new FilesystemStorage(localPath.getParent(), fileLockManager);
309 indexCreationFeature.setLocalIndexPath(storage.getAsset(localPath.getFileName().toString()));
310 } catch (IOException e) {
311 throw new RepositoryException("Could not initialize storage for index path "+localPath);
313 } else if (localPath.isAbsolute()) {
314 indexCreationFeature.setLocalIndexPath(repositoryGroup.getAsset(repoGroupPath.relativize(localPath).toString()));
317 indexCreationFeature.setLocalIndexPath(repositoryGroup.getAsset(localPath.toString()));
320 // References to other repositories are set filled by the registry
324 public RemoteRepositoryConfiguration getRemoteConfiguration(RemoteRepository remoteRepository) throws RepositoryException {
325 if (!(remoteRepository instanceof MavenRemoteRepository)) {
326 log.error("Wrong remote repository type " + remoteRepository.getClass().getName());
327 throw new RepositoryException("The given repository type cannot be handled by the maven provider: " + remoteRepository.getClass().getName());
329 RemoteRepositoryConfiguration cfg = new RemoteRepositoryConfiguration();
330 cfg.setType(remoteRepository.getType().toString());
331 cfg.setId(remoteRepository.getId());
332 cfg.setName(remoteRepository.getName());
333 cfg.setDescription(remoteRepository.getDescription());
334 cfg.setUrl(remoteRepository.getLocation().toString());
335 cfg.setTimeout((int) remoteRepository.getTimeout().toMillis() / 1000);
336 cfg.setCheckPath(remoteRepository.getCheckPath());
337 RepositoryCredentials creds = remoteRepository.getLoginCredentials();
339 if (creds instanceof PasswordCredentials) {
340 PasswordCredentials pCreds = (PasswordCredentials) creds;
341 cfg.setPassword(new String(pCreds.getPassword()));
342 cfg.setUsername(pCreds.getUsername());
345 cfg.setLayout(remoteRepository.getLayout());
346 cfg.setExtraParameters(remoteRepository.getExtraParameters());
347 cfg.setExtraHeaders(remoteRepository.getExtraHeaders());
348 cfg.setRefreshCronExpression(remoteRepository.getSchedulingDefinition());
350 IndexCreationFeature indexCreationFeature = remoteRepository.getFeature(IndexCreationFeature.class).get();
351 cfg.setIndexDir(convertUriToPath(indexCreationFeature.getIndexPath()));
352 cfg.setPackedIndexDir(convertUriToPath(indexCreationFeature.getPackedIndexPath()));
354 RemoteIndexFeature remoteIndexFeature = remoteRepository.getFeature(RemoteIndexFeature.class).get();
355 if (remoteIndexFeature.getIndexUri() != null) {
356 cfg.setRemoteIndexUrl(remoteIndexFeature.getIndexUri().toString());
358 cfg.setRemoteDownloadTimeout((int) remoteIndexFeature.getDownloadTimeout().get(ChronoUnit.SECONDS));
359 cfg.setDownloadRemoteIndexOnStartup(remoteIndexFeature.isDownloadRemoteIndexOnStartup());
360 cfg.setDownloadRemoteIndex(remoteIndexFeature.isDownloadRemoteIndex());
361 cfg.setRemoteDownloadNetworkProxyId(remoteIndexFeature.getProxyId());
362 if (!StringUtils.isEmpty(remoteIndexFeature.getProxyId())) {
363 cfg.setRemoteDownloadNetworkProxyId(remoteIndexFeature.getProxyId());
365 cfg.setRemoteDownloadNetworkProxyId("");
376 public ManagedRepositoryConfiguration getManagedConfiguration(ManagedRepository managedRepository) throws RepositoryException {
377 if (!(managedRepository instanceof MavenManagedRepository || managedRepository instanceof BasicManagedRepository)) {
378 log.error("Wrong remote repository type " + managedRepository.getClass().getName());
379 throw new RepositoryException("The given repository type cannot be handled by the maven provider: " + managedRepository.getClass().getName());
381 ManagedRepositoryConfiguration cfg = new ManagedRepositoryConfiguration();
382 cfg.setType(managedRepository.getType().toString());
383 cfg.setId(managedRepository.getId());
384 cfg.setName(managedRepository.getName());
385 cfg.setDescription(managedRepository.getDescription());
386 cfg.setLocation(convertUriToPath(managedRepository.getLocation()));
387 cfg.setLayout(managedRepository.getLayout());
388 cfg.setRefreshCronExpression(managedRepository.getSchedulingDefinition());
389 cfg.setScanned(managedRepository.isScanned());
390 cfg.setBlockRedeployments(managedRepository.blocksRedeployments());
391 StagingRepositoryFeature stagingRepositoryFeature = managedRepository.getFeature(StagingRepositoryFeature.class).get();
392 cfg.setStageRepoNeeded(stagingRepositoryFeature.isStageRepoNeeded());
393 IndexCreationFeature indexCreationFeature = managedRepository.getFeature(IndexCreationFeature.class).get();
394 cfg.setIndexDir(convertUriToPath(indexCreationFeature.getIndexPath()));
395 cfg.setPackedIndexDir(convertUriToPath(indexCreationFeature.getPackedIndexPath()));
396 cfg.setSkipPackedIndexCreation(indexCreationFeature.isSkipPackedIndexCreation());
398 ArtifactCleanupFeature artifactCleanupFeature = managedRepository.getFeature(ArtifactCleanupFeature.class).get();
399 cfg.setRetentionCount(artifactCleanupFeature.getRetentionCount());
400 cfg.setRetentionPeriod(artifactCleanupFeature.getRetentionPeriod().getDays());
401 cfg.setDeleteReleasedSnapshots(artifactCleanupFeature.isDeleteReleasedSnapshots());
403 if (managedRepository.getActiveReleaseSchemes().contains(ReleaseScheme.RELEASE)) {
404 cfg.setReleases(true);
406 cfg.setReleases(false);
408 if (managedRepository.getActiveReleaseSchemes().contains(ReleaseScheme.SNAPSHOT)) {
409 cfg.setSnapshots(true);
411 cfg.setSnapshots(false);
418 public RepositoryGroupConfiguration getRepositoryGroupConfiguration(RepositoryGroup repositoryGroup) throws RepositoryException {
419 if (repositoryGroup.getType() != RepositoryType.MAVEN) {
420 throw new RepositoryException("The given repository group is not of MAVEN type");
422 RepositoryGroupConfiguration cfg = new RepositoryGroupConfiguration();
423 cfg.setId(repositoryGroup.getId());
424 cfg.setName(repositoryGroup.getName());
425 if (repositoryGroup.supportsFeature( IndexCreationFeature.class ))
427 IndexCreationFeature indexCreationFeature = repositoryGroup.getFeature( IndexCreationFeature.class ).get();
429 cfg.setMergedIndexPath( indexCreationFeature.getIndexPath().toString() );
431 cfg.setMergedIndexTtl(repositoryGroup.getMergedIndexTTL());
432 cfg.setRepositories(repositoryGroup.getRepositories().stream().map(r -> r.getId()).collect(Collectors.toList()));
433 cfg.setCronExpression(repositoryGroup.getSchedulingDefinition());
437 private ManagedRepositoryConfiguration getStageRepoConfig(ManagedRepositoryConfiguration repository) {
438 ManagedRepositoryConfiguration stagingRepository = new ManagedRepositoryConfiguration();
439 stagingRepository.setId(repository.getId() + StagingRepositoryFeature.STAGING_REPO_POSTFIX);
440 stagingRepository.setLayout(repository.getLayout());
441 stagingRepository.setName(repository.getName() + StagingRepositoryFeature.STAGING_REPO_POSTFIX);
442 stagingRepository.setBlockRedeployments(repository.isBlockRedeployments());
443 stagingRepository.setRetentionPeriod(repository.getRetentionPeriod());
444 stagingRepository.setDeleteReleasedSnapshots(repository.isDeleteReleasedSnapshots());
445 stagingRepository.setStageRepoNeeded(false);
447 String path = repository.getLocation();
448 int lastIndex = path.replace('\\', '/').lastIndexOf('/');
449 stagingRepository.setLocation(path.substring(0, lastIndex) + "/" + stagingRepository.getId());
451 if (StringUtils.isNotBlank(repository.getIndexDir())) {
452 Path indexDir = null;
454 indexDir = Paths.get(new URI(repository.getIndexDir().startsWith("file://") ? repository.getIndexDir() : "file://" + repository.getIndexDir()));
455 if (indexDir.isAbsolute()) {
456 Path newDir = indexDir.getParent().resolve(indexDir.getFileName() + StagingRepositoryFeature.STAGING_REPO_POSTFIX);
457 log.debug("Changing index directory {} -> {}", indexDir, newDir);
458 stagingRepository.setIndexDir(newDir.toString());
460 log.debug("Keeping index directory {}", repository.getIndexDir());
461 stagingRepository.setIndexDir(repository.getIndexDir());
463 } catch (URISyntaxException e) {
464 log.error("Could not parse index path as uri {}", repository.getIndexDir());
465 stagingRepository.setIndexDir("");
467 // in case of absolute dir do not use the same
469 if (StringUtils.isNotBlank(repository.getPackedIndexDir())) {
470 Path packedIndexDir = null;
471 packedIndexDir = Paths.get(repository.getPackedIndexDir());
472 if (packedIndexDir.isAbsolute()) {
473 Path newDir = packedIndexDir.getParent().resolve(packedIndexDir.getFileName() + StagingRepositoryFeature.STAGING_REPO_POSTFIX);
474 log.debug("Changing index directory {} -> {}", packedIndexDir, newDir);
475 stagingRepository.setPackedIndexDir(newDir.toString());
477 log.debug("Keeping index directory {}", repository.getPackedIndexDir());
478 stagingRepository.setPackedIndexDir(repository.getPackedIndexDir());
480 // in case of absolute dir do not use the same
482 stagingRepository.setRefreshCronExpression(repository.getRefreshCronExpression());
483 stagingRepository.setReleases(repository.isReleases());
484 stagingRepository.setRetentionCount(repository.getRetentionCount());
485 stagingRepository.setScanned(repository.isScanned());
486 stagingRepository.setSnapshots(repository.isSnapshots());
487 stagingRepository.setSkipPackedIndexCreation(repository.isSkipPackedIndexCreation());
488 // do not duplicate description
489 //stagingRepository.getDescription("")
490 return stagingRepository;
493 private void setBaseConfig(EditableRepository repo, AbstractRepositoryConfiguration cfg) throws RepositoryException {
495 URI baseUri = archivaConfiguration.getRepositoryBaseDir().toUri();
496 repo.setBaseUri(baseUri);
498 repo.setName(repo.getPrimaryLocale(), cfg.getName());
499 repo.setDescription(repo.getPrimaryLocale(), cfg.getDescription());
500 repo.setLayout(cfg.getLayout());
503 public ArchivaConfiguration getArchivaConfiguration() {
504 return archivaConfiguration;
507 public void setArchivaConfiguration(ArchivaConfiguration archivaConfiguration) {
508 this.archivaConfiguration = archivaConfiguration;
512 public void handle(Event event) {