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