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.File; 026import java.io.IOException; 027import java.util.ArrayList; 028import java.util.Collection; 029import java.util.Collections; 030import java.util.IdentityHashMap; 031import java.util.List; 032import java.util.ListIterator; 033import java.util.Set; 034 035import org.eclipse.aether.RepositoryEvent; 036import org.eclipse.aether.RepositoryEvent.EventType; 037import org.eclipse.aether.RepositoryException; 038import org.eclipse.aether.RepositorySystemSession; 039import org.eclipse.aether.RequestTrace; 040import org.eclipse.aether.SyncContext; 041import org.eclipse.aether.artifact.Artifact; 042import org.eclipse.aether.deployment.DeployRequest; 043import org.eclipse.aether.deployment.DeployResult; 044import org.eclipse.aether.deployment.DeploymentException; 045import org.eclipse.aether.impl.Deployer; 046import org.eclipse.aether.impl.MetadataGenerator; 047import org.eclipse.aether.impl.MetadataGeneratorFactory; 048import org.eclipse.aether.impl.OfflineController; 049import org.eclipse.aether.impl.RemoteRepositoryManager; 050import org.eclipse.aether.impl.RepositoryConnectorProvider; 051import org.eclipse.aether.impl.RepositoryEventDispatcher; 052import org.eclipse.aether.impl.UpdateCheck; 053import org.eclipse.aether.impl.UpdateCheckManager; 054import org.eclipse.aether.metadata.MergeableMetadata; 055import org.eclipse.aether.metadata.Metadata; 056import org.eclipse.aether.repository.LocalRepositoryManager; 057import org.eclipse.aether.repository.RemoteRepository; 058import org.eclipse.aether.repository.RepositoryPolicy; 059import org.eclipse.aether.spi.connector.ArtifactUpload; 060import org.eclipse.aether.spi.connector.MetadataDownload; 061import org.eclipse.aether.spi.connector.MetadataUpload; 062import org.eclipse.aether.spi.connector.RepositoryConnector; 063import org.eclipse.aether.spi.io.FileProcessor; 064import org.eclipse.aether.spi.locator.Service; 065import org.eclipse.aether.spi.locator.ServiceLocator; 066import org.eclipse.aether.spi.synccontext.SyncContextFactory; 067import org.eclipse.aether.transfer.ArtifactTransferException; 068import org.eclipse.aether.transfer.MetadataNotFoundException; 069import org.eclipse.aether.transfer.MetadataTransferException; 070import org.eclipse.aether.transfer.NoRepositoryConnectorException; 071import org.eclipse.aether.transfer.RepositoryOfflineException; 072import org.eclipse.aether.transfer.TransferCancelledException; 073import org.eclipse.aether.transfer.TransferEvent; 074import org.eclipse.aether.transform.FileTransformer; 075import org.eclipse.aether.transform.FileTransformerManager; 076 077import static java.util.Objects.requireNonNull; 078 079/** 080 */ 081@Singleton 082@Named 083public class DefaultDeployer implements Deployer, Service { 084 private FileProcessor fileProcessor; 085 086 private RepositoryEventDispatcher repositoryEventDispatcher; 087 088 private RepositoryConnectorProvider repositoryConnectorProvider; 089 090 private RemoteRepositoryManager remoteRepositoryManager; 091 092 private UpdateCheckManager updateCheckManager; 093 094 private Collection<MetadataGeneratorFactory> metadataFactories = new ArrayList<>(); 095 096 private SyncContextFactory syncContextFactory; 097 098 private OfflineController offlineController; 099 100 @Deprecated 101 public DefaultDeployer() { 102 // enables default constructor 103 } 104 105 @SuppressWarnings("checkstyle:parameternumber") 106 @Inject 107 public DefaultDeployer( 108 FileProcessor fileProcessor, 109 RepositoryEventDispatcher repositoryEventDispatcher, 110 RepositoryConnectorProvider repositoryConnectorProvider, 111 RemoteRepositoryManager remoteRepositoryManager, 112 UpdateCheckManager updateCheckManager, 113 Set<MetadataGeneratorFactory> metadataFactories, 114 SyncContextFactory syncContextFactory, 115 OfflineController offlineController) { 116 setFileProcessor(fileProcessor); 117 setRepositoryEventDispatcher(repositoryEventDispatcher); 118 setRepositoryConnectorProvider(repositoryConnectorProvider); 119 setRemoteRepositoryManager(remoteRepositoryManager); 120 setUpdateCheckManager(updateCheckManager); 121 setMetadataGeneratorFactories(metadataFactories); 122 setSyncContextFactory(syncContextFactory); 123 setOfflineController(offlineController); 124 } 125 126 public void initService(ServiceLocator locator) { 127 setFileProcessor(locator.getService(FileProcessor.class)); 128 setRepositoryEventDispatcher(locator.getService(RepositoryEventDispatcher.class)); 129 setRepositoryConnectorProvider(locator.getService(RepositoryConnectorProvider.class)); 130 setRemoteRepositoryManager(locator.getService(RemoteRepositoryManager.class)); 131 setUpdateCheckManager(locator.getService(UpdateCheckManager.class)); 132 setMetadataGeneratorFactories(locator.getServices(MetadataGeneratorFactory.class)); 133 setSyncContextFactory(locator.getService(SyncContextFactory.class)); 134 setOfflineController(locator.getService(OfflineController.class)); 135 } 136 137 public DefaultDeployer setFileProcessor(FileProcessor fileProcessor) { 138 this.fileProcessor = requireNonNull(fileProcessor, "file processor cannot be null"); 139 return this; 140 } 141 142 public DefaultDeployer setRepositoryEventDispatcher(RepositoryEventDispatcher repositoryEventDispatcher) { 143 this.repositoryEventDispatcher = 144 requireNonNull(repositoryEventDispatcher, "repository event dispatcher cannot be null"); 145 return this; 146 } 147 148 public DefaultDeployer setRepositoryConnectorProvider(RepositoryConnectorProvider repositoryConnectorProvider) { 149 this.repositoryConnectorProvider = 150 requireNonNull(repositoryConnectorProvider, "repository connector provider cannot be null"); 151 return this; 152 } 153 154 public DefaultDeployer setRemoteRepositoryManager(RemoteRepositoryManager remoteRepositoryManager) { 155 this.remoteRepositoryManager = 156 requireNonNull(remoteRepositoryManager, "remote repository provider cannot be null"); 157 return this; 158 } 159 160 public DefaultDeployer setUpdateCheckManager(UpdateCheckManager updateCheckManager) { 161 this.updateCheckManager = requireNonNull(updateCheckManager, "update check manager cannot be null"); 162 return this; 163 } 164 165 public DefaultDeployer addMetadataGeneratorFactory(MetadataGeneratorFactory factory) { 166 metadataFactories.add(requireNonNull(factory, "metadata generator factory cannot be null")); 167 return this; 168 } 169 170 public DefaultDeployer setMetadataGeneratorFactories(Collection<MetadataGeneratorFactory> metadataFactories) { 171 if (metadataFactories == null) { 172 this.metadataFactories = new ArrayList<>(); 173 } else { 174 this.metadataFactories = metadataFactories; 175 } 176 return this; 177 } 178 179 public DefaultDeployer setSyncContextFactory(SyncContextFactory syncContextFactory) { 180 this.syncContextFactory = requireNonNull(syncContextFactory, "sync context factory cannot be null"); 181 return this; 182 } 183 184 public DefaultDeployer setOfflineController(OfflineController offlineController) { 185 this.offlineController = requireNonNull(offlineController, "offline controller cannot be null"); 186 return this; 187 } 188 189 public DeployResult deploy(RepositorySystemSession session, DeployRequest request) throws DeploymentException { 190 requireNonNull(session, "session cannot be null"); 191 requireNonNull(request, "request cannot be null"); 192 try { 193 Utils.checkOffline(session, offlineController, request.getRepository()); 194 } catch (RepositoryOfflineException e) { 195 throw new DeploymentException( 196 "Cannot deploy while " + request.getRepository().getId() + " (" 197 + request.getRepository().getUrl() + ") is in offline mode", 198 e); 199 } 200 201 try (SyncContext syncContext = syncContextFactory.newInstance(session, true)) { 202 return deploy(syncContext, session, request); 203 } 204 } 205 206 private DeployResult deploy(SyncContext syncContext, RepositorySystemSession session, DeployRequest request) 207 throws DeploymentException { 208 DeployResult result = new DeployResult(request); 209 210 RequestTrace trace = RequestTrace.newChild(request.getTrace(), request); 211 212 RemoteRepository repository = request.getRepository(); 213 214 RepositoryConnector connector; 215 try { 216 connector = repositoryConnectorProvider.newRepositoryConnector(session, repository); 217 } catch (NoRepositoryConnectorException e) { 218 throw new DeploymentException("Failed to deploy artifacts/metadata: " + e.getMessage(), e); 219 } 220 221 try { 222 List<? extends MetadataGenerator> generators = getMetadataGenerators(session, request); 223 224 FileTransformerManager fileTransformerManager = session.getFileTransformerManager(); 225 226 List<ArtifactUpload> artifactUploads = new ArrayList<>(); 227 List<MetadataUpload> metadataUploads = new ArrayList<>(); 228 IdentityHashMap<Metadata, Object> processedMetadata = new IdentityHashMap<>(); 229 230 EventCatapult catapult = new EventCatapult(session, trace, repository, repositoryEventDispatcher); 231 232 List<Artifact> artifacts = new ArrayList<>(request.getArtifacts()); 233 234 List<Metadata> metadatas = Utils.prepareMetadata(generators, artifacts); 235 236 syncContext.acquire(artifacts, Utils.combine(request.getMetadata(), metadatas)); 237 238 for (Metadata metadata : metadatas) { 239 upload(metadataUploads, session, metadata, repository, connector, catapult); 240 processedMetadata.put(metadata, null); 241 } 242 243 for (ListIterator<Artifact> iterator = artifacts.listIterator(); iterator.hasNext(); ) { 244 Artifact artifact = iterator.next(); 245 246 for (MetadataGenerator generator : generators) { 247 artifact = generator.transformArtifact(artifact); 248 } 249 250 iterator.set(artifact); 251 252 Collection<FileTransformer> fileTransformers = 253 fileTransformerManager.getTransformersForArtifact(artifact); 254 if (!fileTransformers.isEmpty()) { 255 for (FileTransformer fileTransformer : fileTransformers) { 256 Artifact targetArtifact = fileTransformer.transformArtifact(artifact); 257 258 ArtifactUpload upload = new ArtifactUpload(targetArtifact, artifact.getFile(), fileTransformer); 259 upload.setTrace(trace); 260 upload.setListener(new ArtifactUploadListener(catapult, upload)); 261 artifactUploads.add(upload); 262 } 263 } else { 264 ArtifactUpload upload = new ArtifactUpload(artifact, artifact.getFile()); 265 upload.setTrace(trace); 266 upload.setListener(new ArtifactUploadListener(catapult, upload)); 267 artifactUploads.add(upload); 268 } 269 } 270 271 connector.put(artifactUploads, null); 272 273 for (ArtifactUpload upload : artifactUploads) { 274 if (upload.getException() != null) { 275 throw new DeploymentException( 276 "Failed to deploy artifacts: " 277 + upload.getException().getMessage(), 278 upload.getException()); 279 } 280 result.addArtifact(upload.getArtifact()); 281 } 282 283 metadatas = Utils.finishMetadata(generators, artifacts); 284 285 syncContext.acquire(null, metadatas); 286 287 for (Metadata metadata : metadatas) { 288 upload(metadataUploads, session, metadata, repository, connector, catapult); 289 processedMetadata.put(metadata, null); 290 } 291 292 for (Metadata metadata : request.getMetadata()) { 293 if (!processedMetadata.containsKey(metadata)) { 294 upload(metadataUploads, session, metadata, repository, connector, catapult); 295 processedMetadata.put(metadata, null); 296 } 297 } 298 299 connector.put(null, metadataUploads); 300 301 for (MetadataUpload upload : metadataUploads) { 302 if (upload.getException() != null) { 303 throw new DeploymentException( 304 "Failed to deploy metadata: " 305 + upload.getException().getMessage(), 306 upload.getException()); 307 } 308 result.addMetadata(upload.getMetadata()); 309 } 310 } finally { 311 connector.close(); 312 } 313 314 return result; 315 } 316 317 private List<? extends MetadataGenerator> getMetadataGenerators( 318 RepositorySystemSession session, DeployRequest request) { 319 PrioritizedComponents<MetadataGeneratorFactory> factories = 320 Utils.sortMetadataGeneratorFactories(session, this.metadataFactories); 321 322 List<MetadataGenerator> generators = new ArrayList<>(); 323 324 for (PrioritizedComponent<MetadataGeneratorFactory> factory : factories.getEnabled()) { 325 MetadataGenerator generator = factory.getComponent().newInstance(session, request); 326 if (generator != null) { 327 generators.add(generator); 328 } 329 } 330 331 return generators; 332 } 333 334 private void upload( 335 Collection<MetadataUpload> metadataUploads, 336 RepositorySystemSession session, 337 Metadata metadata, 338 RemoteRepository repository, 339 RepositoryConnector connector, 340 EventCatapult catapult) 341 throws DeploymentException { 342 LocalRepositoryManager lrm = session.getLocalRepositoryManager(); 343 File basedir = lrm.getRepository().getBasedir(); 344 345 File dstFile = new File(basedir, lrm.getPathForRemoteMetadata(metadata, repository, "")); 346 347 if (metadata instanceof MergeableMetadata) { 348 if (!((MergeableMetadata) metadata).isMerged()) { 349 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_RESOLVING); 350 event.setTrace(catapult.getTrace()); 351 event.setMetadata(metadata); 352 event.setRepository(repository); 353 repositoryEventDispatcher.dispatch(event.build()); 354 355 event = new RepositoryEvent.Builder(session, EventType.METADATA_DOWNLOADING); 356 event.setTrace(catapult.getTrace()); 357 event.setMetadata(metadata); 358 event.setRepository(repository); 359 repositoryEventDispatcher.dispatch(event.build()); 360 361 RepositoryPolicy policy = getPolicy(session, repository, metadata.getNature()); 362 MetadataDownload download = new MetadataDownload(); 363 download.setMetadata(metadata); 364 download.setFile(dstFile); 365 download.setChecksumPolicy(policy.getChecksumPolicy()); 366 download.setListener(SafeTransferListener.wrap(session)); 367 download.setTrace(catapult.getTrace()); 368 connector.get(null, Collections.singletonList(download)); 369 370 Exception error = download.getException(); 371 372 if (error instanceof MetadataNotFoundException) { 373 dstFile.delete(); 374 } 375 376 event = new RepositoryEvent.Builder(session, EventType.METADATA_DOWNLOADED); 377 event.setTrace(catapult.getTrace()); 378 event.setMetadata(metadata); 379 event.setRepository(repository); 380 event.setException(error); 381 event.setFile(dstFile); 382 repositoryEventDispatcher.dispatch(event.build()); 383 384 event = new RepositoryEvent.Builder(session, EventType.METADATA_RESOLVED); 385 event.setTrace(catapult.getTrace()); 386 event.setMetadata(metadata); 387 event.setRepository(repository); 388 event.setException(error); 389 event.setFile(dstFile); 390 repositoryEventDispatcher.dispatch(event.build()); 391 392 if (error != null && !(error instanceof MetadataNotFoundException)) { 393 throw new DeploymentException( 394 "Failed to retrieve remote metadata " + metadata + ": " + error.getMessage(), error); 395 } 396 } 397 398 try { 399 ((MergeableMetadata) metadata).merge(dstFile, dstFile); 400 } catch (RepositoryException e) { 401 throw new DeploymentException("Failed to update metadata " + metadata + ": " + e.getMessage(), e); 402 } 403 } else { 404 if (metadata.getFile() == null) { 405 throw new DeploymentException("Failed to update metadata " + metadata + ": No file attached."); 406 } 407 try { 408 fileProcessor.copy(metadata.getFile(), dstFile); 409 } catch (IOException e) { 410 throw new DeploymentException("Failed to update metadata " + metadata + ": " + e.getMessage(), e); 411 } 412 } 413 414 UpdateCheck<Metadata, MetadataTransferException> check = new UpdateCheck<>(); 415 check.setItem(metadata); 416 check.setFile(dstFile); 417 check.setRepository(repository); 418 check.setAuthoritativeRepository(repository); 419 updateCheckManager.touchMetadata(session, check); 420 421 MetadataUpload upload = new MetadataUpload(metadata, dstFile); 422 upload.setTrace(catapult.getTrace()); 423 upload.setListener(new MetadataUploadListener(catapult, upload)); 424 metadataUploads.add(upload); 425 } 426 427 private RepositoryPolicy getPolicy( 428 RepositorySystemSession session, RemoteRepository repository, Metadata.Nature nature) { 429 boolean releases = !Metadata.Nature.SNAPSHOT.equals(nature); 430 boolean snapshots = !Metadata.Nature.RELEASE.equals(nature); 431 return remoteRepositoryManager.getPolicy(session, repository, releases, snapshots); 432 } 433 434 static final class EventCatapult { 435 436 private final RepositorySystemSession session; 437 438 private final RequestTrace trace; 439 440 private final RemoteRepository repository; 441 442 private final RepositoryEventDispatcher dispatcher; 443 444 EventCatapult( 445 RepositorySystemSession session, 446 RequestTrace trace, 447 RemoteRepository repository, 448 RepositoryEventDispatcher dispatcher) { 449 this.session = session; 450 this.trace = trace; 451 this.repository = repository; 452 this.dispatcher = dispatcher; 453 } 454 455 public RepositorySystemSession getSession() { 456 return session; 457 } 458 459 public RequestTrace getTrace() { 460 return trace; 461 } 462 463 public void artifactDeploying(Artifact artifact, File file) { 464 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_DEPLOYING); 465 event.setTrace(trace); 466 event.setArtifact(artifact); 467 event.setRepository(repository); 468 event.setFile(file); 469 470 dispatcher.dispatch(event.build()); 471 } 472 473 public void artifactDeployed(Artifact artifact, File file, ArtifactTransferException exception) { 474 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_DEPLOYED); 475 event.setTrace(trace); 476 event.setArtifact(artifact); 477 event.setRepository(repository); 478 event.setFile(file); 479 event.setException(exception); 480 481 dispatcher.dispatch(event.build()); 482 } 483 484 public void metadataDeploying(Metadata metadata, File file) { 485 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_DEPLOYING); 486 event.setTrace(trace); 487 event.setMetadata(metadata); 488 event.setRepository(repository); 489 event.setFile(file); 490 491 dispatcher.dispatch(event.build()); 492 } 493 494 public void metadataDeployed(Metadata metadata, File file, Exception exception) { 495 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_DEPLOYED); 496 event.setTrace(trace); 497 event.setMetadata(metadata); 498 event.setRepository(repository); 499 event.setFile(file); 500 event.setException(exception); 501 502 dispatcher.dispatch(event.build()); 503 } 504 } 505 506 static final class ArtifactUploadListener extends SafeTransferListener { 507 508 private final EventCatapult catapult; 509 510 private final ArtifactUpload transfer; 511 512 ArtifactUploadListener(EventCatapult catapult, ArtifactUpload transfer) { 513 super(catapult.getSession()); 514 this.catapult = catapult; 515 this.transfer = transfer; 516 } 517 518 @Override 519 public void transferInitiated(TransferEvent event) throws TransferCancelledException { 520 super.transferInitiated(event); 521 requireNonNull(event, "event cannot be null"); 522 catapult.artifactDeploying(transfer.getArtifact(), transfer.getFile()); 523 } 524 525 @Override 526 public void transferFailed(TransferEvent event) { 527 super.transferFailed(event); 528 requireNonNull(event, "event cannot be null"); 529 catapult.artifactDeployed(transfer.getArtifact(), transfer.getFile(), transfer.getException()); 530 } 531 532 @Override 533 public void transferSucceeded(TransferEvent event) { 534 super.transferSucceeded(event); 535 requireNonNull(event, "event cannot be null"); 536 catapult.artifactDeployed(transfer.getArtifact(), transfer.getFile(), null); 537 } 538 } 539 540 static final class MetadataUploadListener extends SafeTransferListener { 541 542 private final EventCatapult catapult; 543 544 private final MetadataUpload transfer; 545 546 MetadataUploadListener(EventCatapult catapult, MetadataUpload transfer) { 547 super(catapult.getSession()); 548 this.catapult = catapult; 549 this.transfer = transfer; 550 } 551 552 @Override 553 public void transferInitiated(TransferEvent event) throws TransferCancelledException { 554 super.transferInitiated(event); 555 requireNonNull(event, "event cannot be null"); 556 catapult.metadataDeploying(transfer.getMetadata(), transfer.getFile()); 557 } 558 559 @Override 560 public void transferFailed(TransferEvent event) { 561 super.transferFailed(event); 562 requireNonNull(event, "event cannot be null"); 563 catapult.metadataDeployed(transfer.getMetadata(), transfer.getFile(), transfer.getException()); 564 } 565 566 @Override 567 public void transferSucceeded(TransferEvent event) { 568 super.transferSucceeded(event); 569 requireNonNull(event, "event cannot be null"); 570 catapult.metadataDeployed(transfer.getMetadata(), transfer.getFile(), null); 571 } 572 } 573}