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.ArrayList; 029import java.util.Collection; 030import java.util.Collections; 031import java.util.HashMap; 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.artifact.transformer.ArtifactTransformer; 064import org.eclipse.aether.spi.connector.ArtifactUpload; 065import org.eclipse.aether.spi.connector.MetadataDownload; 066import org.eclipse.aether.spi.connector.MetadataUpload; 067import org.eclipse.aether.spi.connector.RepositoryConnector; 068import org.eclipse.aether.spi.io.PathProcessor; 069import org.eclipse.aether.spi.synccontext.SyncContextFactory; 070import org.eclipse.aether.transfer.ArtifactTransferException; 071import org.eclipse.aether.transfer.MetadataNotFoundException; 072import org.eclipse.aether.transfer.MetadataTransferException; 073import org.eclipse.aether.transfer.NoRepositoryConnectorException; 074import org.eclipse.aether.transfer.RepositoryOfflineException; 075import org.eclipse.aether.transfer.TransferCancelledException; 076import org.eclipse.aether.transfer.TransferEvent; 077import org.slf4j.Logger; 078import org.slf4j.LoggerFactory; 079 080import static java.util.Objects.requireNonNull; 081 082/** 083 */ 084@Singleton 085@Named 086public class DefaultDeployer implements Deployer { 087 private final Logger logger = LoggerFactory.getLogger(getClass()); 088 089 private final PathProcessor pathProcessor; 090 091 private final RepositoryEventDispatcher repositoryEventDispatcher; 092 093 private final RepositoryConnectorProvider repositoryConnectorProvider; 094 095 private final RemoteRepositoryManager remoteRepositoryManager; 096 097 private final UpdateCheckManager updateCheckManager; 098 099 private final Map<String, ArtifactGeneratorFactory> artifactFactories; 100 101 private final Map<String, MetadataGeneratorFactory> metadataFactories; 102 103 private final Map<String, ArtifactTransformer> artifactTransformers; 104 105 private final SyncContextFactory syncContextFactory; 106 107 private final OfflineController offlineController; 108 109 @SuppressWarnings("checkstyle:parameternumber") 110 @Inject 111 public DefaultDeployer( 112 PathProcessor pathProcessor, 113 RepositoryEventDispatcher repositoryEventDispatcher, 114 RepositoryConnectorProvider repositoryConnectorProvider, 115 RemoteRepositoryManager remoteRepositoryManager, 116 UpdateCheckManager updateCheckManager, 117 Map<String, ArtifactGeneratorFactory> artifactFactories, 118 Map<String, MetadataGeneratorFactory> metadataFactories, 119 Map<String, ArtifactTransformer> artifactTransformers, 120 SyncContextFactory syncContextFactory, 121 OfflineController offlineController) { 122 this.pathProcessor = requireNonNull(pathProcessor, "path processor cannot be null"); 123 this.repositoryEventDispatcher = 124 requireNonNull(repositoryEventDispatcher, "repository event dispatcher cannot be null"); 125 this.repositoryConnectorProvider = 126 requireNonNull(repositoryConnectorProvider, "repository connector provider cannot be null"); 127 this.remoteRepositoryManager = 128 requireNonNull(remoteRepositoryManager, "remote repository provider cannot be null"); 129 this.updateCheckManager = requireNonNull(updateCheckManager, "update check manager cannot be null"); 130 this.artifactFactories = Collections.unmodifiableMap(artifactFactories); 131 this.metadataFactories = Collections.unmodifiableMap(metadataFactories); 132 this.artifactTransformers = Collections.unmodifiableMap(artifactTransformers); 133 this.syncContextFactory = requireNonNull(syncContextFactory, "sync context factory cannot be null"); 134 this.offlineController = requireNonNull(offlineController, "offline controller cannot be null"); 135 } 136 137 @Override 138 public DeployResult deploy(RepositorySystemSession session, DeployRequest request) throws DeploymentException { 139 requireNonNull(session, "session cannot be null"); 140 requireNonNull(request, "request cannot be null"); 141 try { 142 Utils.checkOffline(session, offlineController, request.getRepository()); 143 } catch (RepositoryOfflineException e) { 144 throw new DeploymentException( 145 "Cannot deploy while " + request.getRepository().getId() + " (" 146 + request.getRepository().getUrl() + ") is in offline mode", 147 e); 148 } 149 150 for (ArtifactTransformer transformer : artifactTransformers.values()) { 151 request = transformer.transformDeployArtifacts(session, request); 152 } 153 try (SyncContext syncContext = syncContextFactory.newInstance(session, true)) { 154 return deploy(syncContext, session, request); 155 } 156 } 157 158 private DeployResult deploy(SyncContext syncContext, RepositorySystemSession session, DeployRequest request) 159 throws DeploymentException { 160 DeployResult result = new DeployResult(request); 161 162 RequestTrace trace = RequestTrace.newChild(request.getTrace(), request); 163 164 RemoteRepository repository = request.getRepository(); 165 166 RepositoryConnector connector; 167 try { 168 connector = repositoryConnectorProvider.newRepositoryConnector(session, repository); 169 } catch (NoRepositoryConnectorException e) { 170 throw new DeploymentException("Failed to deploy artifacts/metadata: " + e.getMessage(), e); 171 } 172 173 List<Artifact> artifacts = new ArrayList<>(request.getArtifacts()); 174 List<? extends ArtifactGenerator> artifactGenerators = 175 Utils.getArtifactGenerators(session, artifactFactories, request); 176 try { 177 List<Artifact> generatedArtifacts = new ArrayList<>(); 178 for (ArtifactGenerator artifactGenerator : artifactGenerators) { 179 Collection<? extends Artifact> generated = artifactGenerator.generate(generatedArtifacts); 180 for (Artifact generatedArtifact : generated) { 181 Map<String, String> properties = new HashMap<>(generatedArtifact.getProperties()); 182 properties.put( 183 ArtifactGeneratorFactory.ARTIFACT_GENERATOR_ID, 184 requireNonNull(artifactGenerator.generatorId(), "generatorId")); 185 Artifact ga = generatedArtifact.setProperties(properties); 186 generatedArtifacts.add(ga); 187 } 188 } 189 artifacts.addAll(generatedArtifacts); 190 191 List<? extends MetadataGenerator> metadataGenerators = 192 Utils.getMetadataGenerators(session, metadataFactories, request); 193 194 List<ArtifactUpload> artifactUploads = new ArrayList<>(); 195 List<MetadataUpload> metadataUploads = new ArrayList<>(); 196 IdentityHashMap<Metadata, Object> processedMetadata = new IdentityHashMap<>(); 197 198 EventCatapult catapult = new EventCatapult(session, trace, repository, repositoryEventDispatcher); 199 200 List<Metadata> metadatas = Utils.prepareMetadata(metadataGenerators, artifacts); 201 202 syncContext.acquire(artifacts, Utils.combine(request.getMetadata(), metadatas)); 203 204 for (Metadata metadata : metadatas) { 205 upload(metadataUploads, session, metadata, repository, connector, catapult); 206 processedMetadata.put(metadata, null); 207 } 208 209 for (ListIterator<Artifact> iterator = artifacts.listIterator(); iterator.hasNext(); ) { 210 Artifact artifact = iterator.next(); 211 212 for (MetadataGenerator generator : metadataGenerators) { 213 artifact = generator.transformArtifact(artifact); 214 } 215 216 iterator.set(artifact); 217 218 ArtifactUpload upload = new ArtifactUpload(artifact, artifact.getPath()); 219 upload.setTrace(trace); 220 upload.setListener(new ArtifactUploadListener(catapult, upload)); 221 artifactUploads.add(upload); 222 } 223 224 connector.put(artifactUploads, null); 225 226 for (ArtifactUpload upload : artifactUploads) { 227 if (upload.getException() != null) { 228 throw new DeploymentException( 229 "Failed to deploy artifacts: " 230 + upload.getException().getMessage(), 231 upload.getException()); 232 } 233 if (upload.getArtifact().getProperty(ArtifactGeneratorFactory.ARTIFACT_GENERATOR_ID, null) == null) { 234 result.addArtifact(upload.getArtifact()); 235 } 236 } 237 238 metadatas = Utils.finishMetadata(metadataGenerators, artifacts); 239 240 syncContext.acquire(null, metadatas); 241 242 for (Metadata metadata : metadatas) { 243 upload(metadataUploads, session, metadata, repository, connector, catapult); 244 processedMetadata.put(metadata, null); 245 } 246 247 for (Metadata metadata : request.getMetadata()) { 248 if (!processedMetadata.containsKey(metadata)) { 249 upload(metadataUploads, session, metadata, repository, connector, catapult); 250 processedMetadata.put(metadata, null); 251 } 252 } 253 254 connector.put(null, metadataUploads); 255 256 for (MetadataUpload upload : metadataUploads) { 257 if (upload.getException() != null) { 258 throw new DeploymentException( 259 "Failed to deploy metadata: " 260 + upload.getException().getMessage(), 261 upload.getException()); 262 } 263 result.addMetadata(upload.getMetadata()); 264 } 265 } finally { 266 connector.close(); 267 for (ArtifactGenerator artifactGenerator : artifactGenerators) { 268 try { 269 artifactGenerator.close(); 270 } catch (Exception e) { 271 logger.warn("ArtifactGenerator close failure: {}", artifactGenerator.generatorId(), e); 272 } 273 } 274 } 275 276 return result; 277 } 278 279 private void upload( 280 Collection<MetadataUpload> metadataUploads, 281 RepositorySystemSession session, 282 Metadata metadata, 283 RemoteRepository repository, 284 RepositoryConnector connector, 285 EventCatapult catapult) 286 throws DeploymentException { 287 LocalRepositoryManager lrm = session.getLocalRepositoryManager(); 288 Path dstPath = lrm.getAbsolutePathForRemoteMetadata(metadata, repository, ""); 289 290 if (metadata instanceof MergeableMetadata) { 291 if (!((MergeableMetadata) metadata).isMerged()) { 292 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_RESOLVING); 293 event.setTrace(catapult.getTrace()); 294 event.setMetadata(metadata); 295 event.setRepository(repository); 296 repositoryEventDispatcher.dispatch(event.build()); 297 298 event = new RepositoryEvent.Builder(session, EventType.METADATA_DOWNLOADING); 299 event.setTrace(catapult.getTrace()); 300 event.setMetadata(metadata); 301 event.setRepository(repository); 302 repositoryEventDispatcher.dispatch(event.build()); 303 304 RepositoryPolicy policy = getPolicy(session, repository, metadata.getNature()); 305 MetadataDownload download = new MetadataDownload(); 306 download.setMetadata(metadata); 307 download.setPath(dstPath); 308 download.setChecksumPolicy(policy.getChecksumPolicy()); 309 download.setListener(SafeTransferListener.wrap(session)); 310 download.setTrace(catapult.getTrace()); 311 connector.get(null, Collections.singletonList(download)); 312 313 Exception error = download.getException(); 314 315 if (error instanceof MetadataNotFoundException) { 316 try { 317 Files.deleteIfExists(dstPath); 318 } catch (IOException e) { 319 throw new DeploymentException( 320 "Failed to delete cached metadata " + metadata + ": " + e.getMessage(), e); 321 } 322 } 323 324 event = new RepositoryEvent.Builder(session, EventType.METADATA_DOWNLOADED); 325 event.setTrace(catapult.getTrace()); 326 event.setMetadata(metadata); 327 event.setRepository(repository); 328 event.setException(error); 329 event.setPath(dstPath); 330 repositoryEventDispatcher.dispatch(event.build()); 331 332 event = new RepositoryEvent.Builder(session, EventType.METADATA_RESOLVED); 333 event.setTrace(catapult.getTrace()); 334 event.setMetadata(metadata); 335 event.setRepository(repository); 336 event.setException(error); 337 event.setPath(dstPath); 338 repositoryEventDispatcher.dispatch(event.build()); 339 340 if (error != null && !(error instanceof MetadataNotFoundException)) { 341 throw new DeploymentException( 342 "Failed to retrieve remote metadata " + metadata + ": " + error.getMessage(), error); 343 } 344 } 345 346 try { 347 ((MergeableMetadata) metadata).merge(dstPath, dstPath); 348 } catch (RepositoryException e) { 349 throw new DeploymentException("Failed to update metadata " + metadata + ": " + e.getMessage(), e); 350 } 351 } else { 352 if (metadata.getPath() == null) { 353 throw new DeploymentException("Failed to update metadata " + metadata + ": No file attached."); 354 } 355 try { 356 pathProcessor.copy(metadata.getPath(), dstPath); 357 } catch (IOException e) { 358 throw new DeploymentException("Failed to update metadata " + metadata + ": " + e.getMessage(), e); 359 } 360 } 361 362 UpdateCheck<Metadata, MetadataTransferException> check = new UpdateCheck<>(); 363 check.setItem(metadata); 364 check.setPath(dstPath); 365 check.setRepository(repository); 366 check.setAuthoritativeRepository(repository); 367 updateCheckManager.touchMetadata(session, check); 368 369 MetadataUpload upload = new MetadataUpload(metadata, dstPath); 370 upload.setTrace(catapult.getTrace()); 371 upload.setListener(new MetadataUploadListener(catapult, upload)); 372 metadataUploads.add(upload); 373 } 374 375 private RepositoryPolicy getPolicy( 376 RepositorySystemSession session, RemoteRepository repository, Metadata.Nature nature) { 377 boolean releases = !Metadata.Nature.SNAPSHOT.equals(nature); 378 boolean snapshots = !Metadata.Nature.RELEASE.equals(nature); 379 return remoteRepositoryManager.getPolicy(session, repository, releases, snapshots); 380 } 381 382 static final class EventCatapult { 383 384 private final RepositorySystemSession session; 385 386 private final RequestTrace trace; 387 388 private final RemoteRepository repository; 389 390 private final RepositoryEventDispatcher dispatcher; 391 392 EventCatapult( 393 RepositorySystemSession session, 394 RequestTrace trace, 395 RemoteRepository repository, 396 RepositoryEventDispatcher dispatcher) { 397 this.session = session; 398 this.trace = trace; 399 this.repository = repository; 400 this.dispatcher = dispatcher; 401 } 402 403 public RepositorySystemSession getSession() { 404 return session; 405 } 406 407 public RequestTrace getTrace() { 408 return trace; 409 } 410 411 public void artifactDeploying(Artifact artifact, Path path) { 412 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_DEPLOYING); 413 event.setTrace(trace); 414 event.setArtifact(artifact); 415 event.setRepository(repository); 416 event.setPath(path); 417 418 dispatcher.dispatch(event.build()); 419 } 420 421 public void artifactDeployed(Artifact artifact, Path path, ArtifactTransferException exception) { 422 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_DEPLOYED); 423 event.setTrace(trace); 424 event.setArtifact(artifact); 425 event.setRepository(repository); 426 event.setPath(path); 427 event.setException(exception); 428 429 dispatcher.dispatch(event.build()); 430 } 431 432 public void metadataDeploying(Metadata metadata, Path path) { 433 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_DEPLOYING); 434 event.setTrace(trace); 435 event.setMetadata(metadata); 436 event.setRepository(repository); 437 event.setPath(path); 438 439 dispatcher.dispatch(event.build()); 440 } 441 442 public void metadataDeployed(Metadata metadata, Path path, Exception exception) { 443 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_DEPLOYED); 444 event.setTrace(trace); 445 event.setMetadata(metadata); 446 event.setRepository(repository); 447 event.setPath(path); 448 event.setException(exception); 449 450 dispatcher.dispatch(event.build()); 451 } 452 } 453 454 static final class ArtifactUploadListener extends SafeTransferListener { 455 456 private final EventCatapult catapult; 457 458 private final ArtifactUpload transfer; 459 460 ArtifactUploadListener(EventCatapult catapult, ArtifactUpload transfer) { 461 super(catapult.getSession()); 462 this.catapult = catapult; 463 this.transfer = transfer; 464 } 465 466 @Override 467 public void transferInitiated(TransferEvent event) throws TransferCancelledException { 468 super.transferInitiated(event); 469 requireNonNull(event, "event cannot be null"); 470 catapult.artifactDeploying(transfer.getArtifact(), transfer.getPath()); 471 } 472 473 @Override 474 public void transferFailed(TransferEvent event) { 475 super.transferFailed(event); 476 requireNonNull(event, "event cannot be null"); 477 catapult.artifactDeployed(transfer.getArtifact(), transfer.getPath(), transfer.getException()); 478 } 479 480 @Override 481 public void transferSucceeded(TransferEvent event) { 482 super.transferSucceeded(event); 483 requireNonNull(event, "event cannot be null"); 484 catapult.artifactDeployed(transfer.getArtifact(), transfer.getPath(), null); 485 } 486 } 487 488 static final class MetadataUploadListener extends SafeTransferListener { 489 490 private final EventCatapult catapult; 491 492 private final MetadataUpload transfer; 493 494 MetadataUploadListener(EventCatapult catapult, MetadataUpload transfer) { 495 super(catapult.getSession()); 496 this.catapult = catapult; 497 this.transfer = transfer; 498 } 499 500 @Override 501 public void transferInitiated(TransferEvent event) throws TransferCancelledException { 502 super.transferInitiated(event); 503 requireNonNull(event, "event cannot be null"); 504 catapult.metadataDeploying(transfer.getMetadata(), transfer.getPath()); 505 } 506 507 @Override 508 public void transferFailed(TransferEvent event) { 509 super.transferFailed(event); 510 requireNonNull(event, "event cannot be null"); 511 catapult.metadataDeployed(transfer.getMetadata(), transfer.getPath(), transfer.getException()); 512 } 513 514 @Override 515 public void transferSucceeded(TransferEvent event) { 516 super.transferSucceeded(event); 517 requireNonNull(event, "event cannot be null"); 518 catapult.metadataDeployed(transfer.getMetadata(), transfer.getPath(), null); 519 } 520 } 521}