001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.eclipse.aether.internal.impl; 020 021import javax.inject.Inject; 022import javax.inject.Named; 023import javax.inject.Singleton; 024 025import java.io.IOException; 026import java.nio.file.Files; 027import java.nio.file.Path; 028import java.util.*; 029import java.util.ArrayList; 030import java.util.Collection; 031import java.util.Collections; 032import java.util.IdentityHashMap; 033import java.util.List; 034import java.util.ListIterator; 035import java.util.Map; 036 037import org.eclipse.aether.RepositoryEvent; 038import org.eclipse.aether.RepositoryEvent.EventType; 039import org.eclipse.aether.RepositoryException; 040import org.eclipse.aether.RepositorySystemSession; 041import org.eclipse.aether.RequestTrace; 042import org.eclipse.aether.SyncContext; 043import org.eclipse.aether.artifact.Artifact; 044import org.eclipse.aether.deployment.DeployRequest; 045import org.eclipse.aether.deployment.DeployResult; 046import org.eclipse.aether.deployment.DeploymentException; 047import org.eclipse.aether.impl.Deployer; 048import org.eclipse.aether.impl.MetadataGenerator; 049import org.eclipse.aether.impl.MetadataGeneratorFactory; 050import org.eclipse.aether.impl.OfflineController; 051import org.eclipse.aether.impl.RemoteRepositoryManager; 052import org.eclipse.aether.impl.RepositoryConnectorProvider; 053import org.eclipse.aether.impl.RepositoryEventDispatcher; 054import org.eclipse.aether.impl.UpdateCheck; 055import org.eclipse.aether.impl.UpdateCheckManager; 056import org.eclipse.aether.metadata.MergeableMetadata; 057import org.eclipse.aether.metadata.Metadata; 058import org.eclipse.aether.repository.LocalRepositoryManager; 059import org.eclipse.aether.repository.RemoteRepository; 060import org.eclipse.aether.repository.RepositoryPolicy; 061import org.eclipse.aether.spi.artifact.generator.ArtifactGenerator; 062import org.eclipse.aether.spi.artifact.generator.ArtifactGeneratorFactory; 063import org.eclipse.aether.spi.connector.ArtifactUpload; 064import org.eclipse.aether.spi.connector.MetadataDownload; 065import org.eclipse.aether.spi.connector.MetadataUpload; 066import org.eclipse.aether.spi.connector.RepositoryConnector; 067import org.eclipse.aether.spi.io.PathProcessor; 068import org.eclipse.aether.spi.synccontext.SyncContextFactory; 069import org.eclipse.aether.transfer.ArtifactTransferException; 070import org.eclipse.aether.transfer.MetadataNotFoundException; 071import org.eclipse.aether.transfer.MetadataTransferException; 072import org.eclipse.aether.transfer.NoRepositoryConnectorException; 073import org.eclipse.aether.transfer.RepositoryOfflineException; 074import org.eclipse.aether.transfer.TransferCancelledException; 075import org.eclipse.aether.transfer.TransferEvent; 076import org.slf4j.Logger; 077import org.slf4j.LoggerFactory; 078 079import static java.util.Objects.requireNonNull; 080 081/** 082 */ 083@Singleton 084@Named 085public class DefaultDeployer implements Deployer { 086 private final Logger logger = LoggerFactory.getLogger(getClass()); 087 088 private final PathProcessor pathProcessor; 089 090 private final RepositoryEventDispatcher repositoryEventDispatcher; 091 092 private final RepositoryConnectorProvider repositoryConnectorProvider; 093 094 private final RemoteRepositoryManager remoteRepositoryManager; 095 096 private final UpdateCheckManager updateCheckManager; 097 098 private final Map<String, ArtifactGeneratorFactory> artifactFactories; 099 100 private final Map<String, MetadataGeneratorFactory> metadataFactories; 101 102 private final SyncContextFactory syncContextFactory; 103 104 private final OfflineController offlineController; 105 106 @SuppressWarnings("checkstyle:parameternumber") 107 @Inject 108 public DefaultDeployer( 109 PathProcessor pathProcessor, 110 RepositoryEventDispatcher repositoryEventDispatcher, 111 RepositoryConnectorProvider repositoryConnectorProvider, 112 RemoteRepositoryManager remoteRepositoryManager, 113 UpdateCheckManager updateCheckManager, 114 Map<String, ArtifactGeneratorFactory> artifactFactories, 115 Map<String, MetadataGeneratorFactory> metadataFactories, 116 SyncContextFactory syncContextFactory, 117 OfflineController offlineController) { 118 this.pathProcessor = requireNonNull(pathProcessor, "path processor cannot be null"); 119 this.repositoryEventDispatcher = 120 requireNonNull(repositoryEventDispatcher, "repository event dispatcher cannot be null"); 121 this.repositoryConnectorProvider = 122 requireNonNull(repositoryConnectorProvider, "repository connector provider cannot be null"); 123 this.remoteRepositoryManager = 124 requireNonNull(remoteRepositoryManager, "remote repository provider cannot be null"); 125 this.updateCheckManager = requireNonNull(updateCheckManager, "update check manager cannot be null"); 126 this.artifactFactories = Collections.unmodifiableMap(artifactFactories); 127 this.metadataFactories = Collections.unmodifiableMap(metadataFactories); 128 this.syncContextFactory = requireNonNull(syncContextFactory, "sync context factory cannot be null"); 129 this.offlineController = requireNonNull(offlineController, "offline controller cannot be null"); 130 } 131 132 @Override 133 public DeployResult deploy(RepositorySystemSession session, DeployRequest request) throws DeploymentException { 134 requireNonNull(session, "session cannot be null"); 135 requireNonNull(request, "request cannot be null"); 136 try { 137 Utils.checkOffline(session, offlineController, request.getRepository()); 138 } catch (RepositoryOfflineException e) { 139 throw new DeploymentException( 140 "Cannot deploy while " + request.getRepository().getId() + " (" 141 + request.getRepository().getUrl() + ") is in offline mode", 142 e); 143 } 144 145 try (SyncContext syncContext = syncContextFactory.newInstance(session, true)) { 146 return deploy(syncContext, session, request); 147 } 148 } 149 150 private DeployResult deploy(SyncContext syncContext, RepositorySystemSession session, DeployRequest request) 151 throws DeploymentException { 152 DeployResult result = new DeployResult(request); 153 154 RequestTrace trace = RequestTrace.newChild(request.getTrace(), request); 155 156 RemoteRepository repository = request.getRepository(); 157 158 RepositoryConnector connector; 159 try { 160 connector = repositoryConnectorProvider.newRepositoryConnector(session, repository); 161 } catch (NoRepositoryConnectorException e) { 162 throw new DeploymentException("Failed to deploy artifacts/metadata: " + e.getMessage(), e); 163 } 164 165 List<Artifact> artifacts = new ArrayList<>(request.getArtifacts()); 166 List<? extends ArtifactGenerator> artifactGenerators = getArtifactGenerators(session, request); 167 try { 168 List<Artifact> generatedArtifacts = new ArrayList<>(); 169 for (ArtifactGenerator artifactGenerator : artifactGenerators) { 170 Collection<? extends Artifact> generated = artifactGenerator.generate(generatedArtifacts); 171 for (Artifact generatedArtifact : generated) { 172 Map<String, String> properties = new HashMap<>(generatedArtifact.getProperties()); 173 properties.put( 174 ArtifactGeneratorFactory.ARTIFACT_GENERATOR_ID, 175 requireNonNull(artifactGenerator.generatorId(), "generatorId")); 176 Artifact ga = generatedArtifact.setProperties(properties); 177 generatedArtifacts.add(ga); 178 } 179 } 180 artifacts.addAll(generatedArtifacts); 181 182 List<? extends MetadataGenerator> generators = getMetadataGenerators(session, request); 183 184 List<ArtifactUpload> artifactUploads = new ArrayList<>(); 185 List<MetadataUpload> metadataUploads = new ArrayList<>(); 186 IdentityHashMap<Metadata, Object> processedMetadata = new IdentityHashMap<>(); 187 188 EventCatapult catapult = new EventCatapult(session, trace, repository, repositoryEventDispatcher); 189 190 List<Metadata> metadatas = Utils.prepareMetadata(generators, artifacts); 191 192 syncContext.acquire(artifacts, Utils.combine(request.getMetadata(), metadatas)); 193 194 for (Metadata metadata : metadatas) { 195 upload(metadataUploads, session, metadata, repository, connector, catapult); 196 processedMetadata.put(metadata, null); 197 } 198 199 for (ListIterator<Artifact> iterator = artifacts.listIterator(); iterator.hasNext(); ) { 200 Artifact artifact = iterator.next(); 201 202 for (MetadataGenerator generator : generators) { 203 artifact = generator.transformArtifact(artifact); 204 } 205 206 iterator.set(artifact); 207 208 ArtifactUpload upload = new ArtifactUpload(artifact, artifact.getPath()); 209 upload.setTrace(trace); 210 upload.setListener(new ArtifactUploadListener(catapult, upload)); 211 artifactUploads.add(upload); 212 } 213 214 connector.put(artifactUploads, null); 215 216 for (ArtifactUpload upload : artifactUploads) { 217 if (upload.getException() != null) { 218 throw new DeploymentException( 219 "Failed to deploy artifacts: " 220 + upload.getException().getMessage(), 221 upload.getException()); 222 } 223 if (upload.getArtifact().getProperty(ArtifactGeneratorFactory.ARTIFACT_GENERATOR_ID, null) == null) { 224 result.addArtifact(upload.getArtifact()); 225 } 226 } 227 228 metadatas = Utils.finishMetadata(generators, artifacts); 229 230 syncContext.acquire(null, metadatas); 231 232 for (Metadata metadata : metadatas) { 233 upload(metadataUploads, session, metadata, repository, connector, catapult); 234 processedMetadata.put(metadata, null); 235 } 236 237 for (Metadata metadata : request.getMetadata()) { 238 if (!processedMetadata.containsKey(metadata)) { 239 upload(metadataUploads, session, metadata, repository, connector, catapult); 240 processedMetadata.put(metadata, null); 241 } 242 } 243 244 connector.put(null, metadataUploads); 245 246 for (MetadataUpload upload : metadataUploads) { 247 if (upload.getException() != null) { 248 throw new DeploymentException( 249 "Failed to deploy metadata: " 250 + upload.getException().getMessage(), 251 upload.getException()); 252 } 253 result.addMetadata(upload.getMetadata()); 254 } 255 } finally { 256 connector.close(); 257 for (ArtifactGenerator artifactGenerator : artifactGenerators) { 258 try { 259 artifactGenerator.close(); 260 } catch (Exception e) { 261 logger.warn("ArtifactGenerator close failure: {}", artifactGenerator.generatorId(), e); 262 } 263 } 264 } 265 266 return result; 267 } 268 269 private List<? extends ArtifactGenerator> getArtifactGenerators( 270 RepositorySystemSession session, DeployRequest request) { 271 PrioritizedComponents<ArtifactGeneratorFactory> factories = 272 Utils.sortArtifactGeneratorFactories(session, artifactFactories); 273 274 List<ArtifactGenerator> generators = new ArrayList<>(); 275 276 for (PrioritizedComponent<ArtifactGeneratorFactory> factory : factories.getEnabled()) { 277 ArtifactGenerator generator = factory.getComponent().newInstance(session, request); 278 if (generator != null) { 279 generators.add(generator); 280 } 281 } 282 283 return generators; 284 } 285 286 private List<? extends MetadataGenerator> getMetadataGenerators( 287 RepositorySystemSession session, DeployRequest request) { 288 PrioritizedComponents<MetadataGeneratorFactory> factories = 289 Utils.sortMetadataGeneratorFactories(session, metadataFactories); 290 291 List<MetadataGenerator> generators = new ArrayList<>(); 292 293 for (PrioritizedComponent<MetadataGeneratorFactory> factory : factories.getEnabled()) { 294 MetadataGenerator generator = factory.getComponent().newInstance(session, request); 295 if (generator != null) { 296 generators.add(generator); 297 } 298 } 299 300 return generators; 301 } 302 303 private void upload( 304 Collection<MetadataUpload> metadataUploads, 305 RepositorySystemSession session, 306 Metadata metadata, 307 RemoteRepository repository, 308 RepositoryConnector connector, 309 EventCatapult catapult) 310 throws DeploymentException { 311 LocalRepositoryManager lrm = session.getLocalRepositoryManager(); 312 Path basePath = lrm.getRepository().getBasePath(); 313 314 Path dstPath = basePath.resolve(lrm.getPathForRemoteMetadata(metadata, repository, "")); 315 316 if (metadata instanceof MergeableMetadata) { 317 if (!((MergeableMetadata) metadata).isMerged()) { 318 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_RESOLVING); 319 event.setTrace(catapult.getTrace()); 320 event.setMetadata(metadata); 321 event.setRepository(repository); 322 repositoryEventDispatcher.dispatch(event.build()); 323 324 event = new RepositoryEvent.Builder(session, EventType.METADATA_DOWNLOADING); 325 event.setTrace(catapult.getTrace()); 326 event.setMetadata(metadata); 327 event.setRepository(repository); 328 repositoryEventDispatcher.dispatch(event.build()); 329 330 RepositoryPolicy policy = getPolicy(session, repository, metadata.getNature()); 331 MetadataDownload download = new MetadataDownload(); 332 download.setMetadata(metadata); 333 download.setPath(dstPath); 334 download.setChecksumPolicy(policy.getChecksumPolicy()); 335 download.setListener(SafeTransferListener.wrap(session)); 336 download.setTrace(catapult.getTrace()); 337 connector.get(null, Collections.singletonList(download)); 338 339 Exception error = download.getException(); 340 341 if (error instanceof MetadataNotFoundException) { 342 try { 343 Files.deleteIfExists(dstPath); 344 } catch (IOException e) { 345 throw new DeploymentException( 346 "Failed to delete cached metadata " + metadata + ": " + e.getMessage(), e); 347 } 348 } 349 350 event = new RepositoryEvent.Builder(session, EventType.METADATA_DOWNLOADED); 351 event.setTrace(catapult.getTrace()); 352 event.setMetadata(metadata); 353 event.setRepository(repository); 354 event.setException(error); 355 event.setPath(dstPath); 356 repositoryEventDispatcher.dispatch(event.build()); 357 358 event = new RepositoryEvent.Builder(session, EventType.METADATA_RESOLVED); 359 event.setTrace(catapult.getTrace()); 360 event.setMetadata(metadata); 361 event.setRepository(repository); 362 event.setException(error); 363 event.setPath(dstPath); 364 repositoryEventDispatcher.dispatch(event.build()); 365 366 if (error != null && !(error instanceof MetadataNotFoundException)) { 367 throw new DeploymentException( 368 "Failed to retrieve remote metadata " + metadata + ": " + error.getMessage(), error); 369 } 370 } 371 372 try { 373 ((MergeableMetadata) metadata).merge(dstPath, dstPath); 374 } catch (RepositoryException e) { 375 throw new DeploymentException("Failed to update metadata " + metadata + ": " + e.getMessage(), e); 376 } 377 } else { 378 if (metadata.getPath() == null) { 379 throw new DeploymentException("Failed to update metadata " + metadata + ": No file attached."); 380 } 381 try { 382 pathProcessor.copy(metadata.getPath(), dstPath); 383 } catch (IOException e) { 384 throw new DeploymentException("Failed to update metadata " + metadata + ": " + e.getMessage(), e); 385 } 386 } 387 388 UpdateCheck<Metadata, MetadataTransferException> check = new UpdateCheck<>(); 389 check.setItem(metadata); 390 check.setPath(dstPath); 391 check.setRepository(repository); 392 check.setAuthoritativeRepository(repository); 393 updateCheckManager.touchMetadata(session, check); 394 395 MetadataUpload upload = new MetadataUpload(metadata, dstPath); 396 upload.setTrace(catapult.getTrace()); 397 upload.setListener(new MetadataUploadListener(catapult, upload)); 398 metadataUploads.add(upload); 399 } 400 401 private RepositoryPolicy getPolicy( 402 RepositorySystemSession session, RemoteRepository repository, Metadata.Nature nature) { 403 boolean releases = !Metadata.Nature.SNAPSHOT.equals(nature); 404 boolean snapshots = !Metadata.Nature.RELEASE.equals(nature); 405 return remoteRepositoryManager.getPolicy(session, repository, releases, snapshots); 406 } 407 408 static final class EventCatapult { 409 410 private final RepositorySystemSession session; 411 412 private final RequestTrace trace; 413 414 private final RemoteRepository repository; 415 416 private final RepositoryEventDispatcher dispatcher; 417 418 EventCatapult( 419 RepositorySystemSession session, 420 RequestTrace trace, 421 RemoteRepository repository, 422 RepositoryEventDispatcher dispatcher) { 423 this.session = session; 424 this.trace = trace; 425 this.repository = repository; 426 this.dispatcher = dispatcher; 427 } 428 429 public RepositorySystemSession getSession() { 430 return session; 431 } 432 433 public RequestTrace getTrace() { 434 return trace; 435 } 436 437 public void artifactDeploying(Artifact artifact, Path path) { 438 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_DEPLOYING); 439 event.setTrace(trace); 440 event.setArtifact(artifact); 441 event.setRepository(repository); 442 event.setPath(path); 443 444 dispatcher.dispatch(event.build()); 445 } 446 447 public void artifactDeployed(Artifact artifact, Path path, ArtifactTransferException exception) { 448 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_DEPLOYED); 449 event.setTrace(trace); 450 event.setArtifact(artifact); 451 event.setRepository(repository); 452 event.setPath(path); 453 event.setException(exception); 454 455 dispatcher.dispatch(event.build()); 456 } 457 458 public void metadataDeploying(Metadata metadata, Path path) { 459 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_DEPLOYING); 460 event.setTrace(trace); 461 event.setMetadata(metadata); 462 event.setRepository(repository); 463 event.setPath(path); 464 465 dispatcher.dispatch(event.build()); 466 } 467 468 public void metadataDeployed(Metadata metadata, Path path, Exception exception) { 469 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_DEPLOYED); 470 event.setTrace(trace); 471 event.setMetadata(metadata); 472 event.setRepository(repository); 473 event.setPath(path); 474 event.setException(exception); 475 476 dispatcher.dispatch(event.build()); 477 } 478 } 479 480 static final class ArtifactUploadListener extends SafeTransferListener { 481 482 private final EventCatapult catapult; 483 484 private final ArtifactUpload transfer; 485 486 ArtifactUploadListener(EventCatapult catapult, ArtifactUpload transfer) { 487 super(catapult.getSession()); 488 this.catapult = catapult; 489 this.transfer = transfer; 490 } 491 492 @Override 493 public void transferInitiated(TransferEvent event) throws TransferCancelledException { 494 super.transferInitiated(event); 495 requireNonNull(event, "event cannot be null"); 496 catapult.artifactDeploying(transfer.getArtifact(), transfer.getPath()); 497 } 498 499 @Override 500 public void transferFailed(TransferEvent event) { 501 super.transferFailed(event); 502 requireNonNull(event, "event cannot be null"); 503 catapult.artifactDeployed(transfer.getArtifact(), transfer.getPath(), transfer.getException()); 504 } 505 506 @Override 507 public void transferSucceeded(TransferEvent event) { 508 super.transferSucceeded(event); 509 requireNonNull(event, "event cannot be null"); 510 catapult.artifactDeployed(transfer.getArtifact(), transfer.getPath(), null); 511 } 512 } 513 514 static final class MetadataUploadListener extends SafeTransferListener { 515 516 private final EventCatapult catapult; 517 518 private final MetadataUpload transfer; 519 520 MetadataUploadListener(EventCatapult catapult, MetadataUpload transfer) { 521 super(catapult.getSession()); 522 this.catapult = catapult; 523 this.transfer = transfer; 524 } 525 526 @Override 527 public void transferInitiated(TransferEvent event) throws TransferCancelledException { 528 super.transferInitiated(event); 529 requireNonNull(event, "event cannot be null"); 530 catapult.metadataDeploying(transfer.getMetadata(), transfer.getPath()); 531 } 532 533 @Override 534 public void transferFailed(TransferEvent event) { 535 super.transferFailed(event); 536 requireNonNull(event, "event cannot be null"); 537 catapult.metadataDeployed(transfer.getMetadata(), transfer.getPath(), transfer.getException()); 538 } 539 540 @Override 541 public void transferSucceeded(TransferEvent event) { 542 super.transferSucceeded(event); 543 requireNonNull(event, "event cannot be null"); 544 catapult.metadataDeployed(transfer.getMetadata(), transfer.getPath(), null); 545 } 546 } 547}