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.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 = 167 Utils.getArtifactGenerators(session, artifactFactories, request); 168 try { 169 List<Artifact> generatedArtifacts = new ArrayList<>(); 170 for (ArtifactGenerator artifactGenerator : artifactGenerators) { 171 Collection<? extends Artifact> generated = artifactGenerator.generate(generatedArtifacts); 172 for (Artifact generatedArtifact : generated) { 173 Map<String, String> properties = new HashMap<>(generatedArtifact.getProperties()); 174 properties.put( 175 ArtifactGeneratorFactory.ARTIFACT_GENERATOR_ID, 176 requireNonNull(artifactGenerator.generatorId(), "generatorId")); 177 Artifact ga = generatedArtifact.setProperties(properties); 178 generatedArtifacts.add(ga); 179 } 180 } 181 artifacts.addAll(generatedArtifacts); 182 183 List<? extends MetadataGenerator> metadataGenerators = 184 Utils.getMetadataGenerators(session, metadataFactories, request); 185 186 List<ArtifactUpload> artifactUploads = new ArrayList<>(); 187 List<MetadataUpload> metadataUploads = new ArrayList<>(); 188 IdentityHashMap<Metadata, Object> processedMetadata = new IdentityHashMap<>(); 189 190 EventCatapult catapult = new EventCatapult(session, trace, repository, repositoryEventDispatcher); 191 192 List<Metadata> metadatas = Utils.prepareMetadata(metadataGenerators, artifacts); 193 194 syncContext.acquire(artifacts, Utils.combine(request.getMetadata(), metadatas)); 195 196 for (Metadata metadata : metadatas) { 197 upload(metadataUploads, session, metadata, repository, connector, catapult); 198 processedMetadata.put(metadata, null); 199 } 200 201 for (ListIterator<Artifact> iterator = artifacts.listIterator(); iterator.hasNext(); ) { 202 Artifact artifact = iterator.next(); 203 204 for (MetadataGenerator generator : metadataGenerators) { 205 artifact = generator.transformArtifact(artifact); 206 } 207 208 iterator.set(artifact); 209 210 ArtifactUpload upload = new ArtifactUpload(artifact, artifact.getPath()); 211 upload.setTrace(trace); 212 upload.setListener(new ArtifactUploadListener(catapult, upload)); 213 artifactUploads.add(upload); 214 } 215 216 connector.put(artifactUploads, null); 217 218 for (ArtifactUpload upload : artifactUploads) { 219 if (upload.getException() != null) { 220 throw new DeploymentException( 221 "Failed to deploy artifacts: " 222 + upload.getException().getMessage(), 223 upload.getException()); 224 } 225 if (upload.getArtifact().getProperty(ArtifactGeneratorFactory.ARTIFACT_GENERATOR_ID, null) == null) { 226 result.addArtifact(upload.getArtifact()); 227 } 228 } 229 230 metadatas = Utils.finishMetadata(metadataGenerators, artifacts); 231 232 syncContext.acquire(null, metadatas); 233 234 for (Metadata metadata : metadatas) { 235 upload(metadataUploads, session, metadata, repository, connector, catapult); 236 processedMetadata.put(metadata, null); 237 } 238 239 for (Metadata metadata : request.getMetadata()) { 240 if (!processedMetadata.containsKey(metadata)) { 241 upload(metadataUploads, session, metadata, repository, connector, catapult); 242 processedMetadata.put(metadata, null); 243 } 244 } 245 246 connector.put(null, metadataUploads); 247 248 for (MetadataUpload upload : metadataUploads) { 249 if (upload.getException() != null) { 250 throw new DeploymentException( 251 "Failed to deploy metadata: " 252 + upload.getException().getMessage(), 253 upload.getException()); 254 } 255 result.addMetadata(upload.getMetadata()); 256 } 257 } finally { 258 connector.close(); 259 for (ArtifactGenerator artifactGenerator : artifactGenerators) { 260 try { 261 artifactGenerator.close(); 262 } catch (Exception e) { 263 logger.warn("ArtifactGenerator close failure: {}", artifactGenerator.generatorId(), e); 264 } 265 } 266 } 267 268 return result; 269 } 270 271 private void upload( 272 Collection<MetadataUpload> metadataUploads, 273 RepositorySystemSession session, 274 Metadata metadata, 275 RemoteRepository repository, 276 RepositoryConnector connector, 277 EventCatapult catapult) 278 throws DeploymentException { 279 LocalRepositoryManager lrm = session.getLocalRepositoryManager(); 280 Path basePath = lrm.getRepository().getBasePath(); 281 282 Path dstPath = basePath.resolve(lrm.getPathForRemoteMetadata(metadata, repository, "")); 283 284 if (metadata instanceof MergeableMetadata) { 285 if (!((MergeableMetadata) metadata).isMerged()) { 286 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_RESOLVING); 287 event.setTrace(catapult.getTrace()); 288 event.setMetadata(metadata); 289 event.setRepository(repository); 290 repositoryEventDispatcher.dispatch(event.build()); 291 292 event = new RepositoryEvent.Builder(session, EventType.METADATA_DOWNLOADING); 293 event.setTrace(catapult.getTrace()); 294 event.setMetadata(metadata); 295 event.setRepository(repository); 296 repositoryEventDispatcher.dispatch(event.build()); 297 298 RepositoryPolicy policy = getPolicy(session, repository, metadata.getNature()); 299 MetadataDownload download = new MetadataDownload(); 300 download.setMetadata(metadata); 301 download.setPath(dstPath); 302 download.setChecksumPolicy(policy.getChecksumPolicy()); 303 download.setListener(SafeTransferListener.wrap(session)); 304 download.setTrace(catapult.getTrace()); 305 connector.get(null, Collections.singletonList(download)); 306 307 Exception error = download.getException(); 308 309 if (error instanceof MetadataNotFoundException) { 310 try { 311 Files.deleteIfExists(dstPath); 312 } catch (IOException e) { 313 throw new DeploymentException( 314 "Failed to delete cached metadata " + metadata + ": " + e.getMessage(), e); 315 } 316 } 317 318 event = new RepositoryEvent.Builder(session, EventType.METADATA_DOWNLOADED); 319 event.setTrace(catapult.getTrace()); 320 event.setMetadata(metadata); 321 event.setRepository(repository); 322 event.setException(error); 323 event.setPath(dstPath); 324 repositoryEventDispatcher.dispatch(event.build()); 325 326 event = new RepositoryEvent.Builder(session, EventType.METADATA_RESOLVED); 327 event.setTrace(catapult.getTrace()); 328 event.setMetadata(metadata); 329 event.setRepository(repository); 330 event.setException(error); 331 event.setPath(dstPath); 332 repositoryEventDispatcher.dispatch(event.build()); 333 334 if (error != null && !(error instanceof MetadataNotFoundException)) { 335 throw new DeploymentException( 336 "Failed to retrieve remote metadata " + metadata + ": " + error.getMessage(), error); 337 } 338 } 339 340 try { 341 ((MergeableMetadata) metadata).merge(dstPath, dstPath); 342 } catch (RepositoryException e) { 343 throw new DeploymentException("Failed to update metadata " + metadata + ": " + e.getMessage(), e); 344 } 345 } else { 346 if (metadata.getPath() == null) { 347 throw new DeploymentException("Failed to update metadata " + metadata + ": No file attached."); 348 } 349 try { 350 pathProcessor.copy(metadata.getPath(), dstPath); 351 } catch (IOException e) { 352 throw new DeploymentException("Failed to update metadata " + metadata + ": " + e.getMessage(), e); 353 } 354 } 355 356 UpdateCheck<Metadata, MetadataTransferException> check = new UpdateCheck<>(); 357 check.setItem(metadata); 358 check.setPath(dstPath); 359 check.setRepository(repository); 360 check.setAuthoritativeRepository(repository); 361 updateCheckManager.touchMetadata(session, check); 362 363 MetadataUpload upload = new MetadataUpload(metadata, dstPath); 364 upload.setTrace(catapult.getTrace()); 365 upload.setListener(new MetadataUploadListener(catapult, upload)); 366 metadataUploads.add(upload); 367 } 368 369 private RepositoryPolicy getPolicy( 370 RepositorySystemSession session, RemoteRepository repository, Metadata.Nature nature) { 371 boolean releases = !Metadata.Nature.SNAPSHOT.equals(nature); 372 boolean snapshots = !Metadata.Nature.RELEASE.equals(nature); 373 return remoteRepositoryManager.getPolicy(session, repository, releases, snapshots); 374 } 375 376 static final class EventCatapult { 377 378 private final RepositorySystemSession session; 379 380 private final RequestTrace trace; 381 382 private final RemoteRepository repository; 383 384 private final RepositoryEventDispatcher dispatcher; 385 386 EventCatapult( 387 RepositorySystemSession session, 388 RequestTrace trace, 389 RemoteRepository repository, 390 RepositoryEventDispatcher dispatcher) { 391 this.session = session; 392 this.trace = trace; 393 this.repository = repository; 394 this.dispatcher = dispatcher; 395 } 396 397 public RepositorySystemSession getSession() { 398 return session; 399 } 400 401 public RequestTrace getTrace() { 402 return trace; 403 } 404 405 public void artifactDeploying(Artifact artifact, Path path) { 406 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_DEPLOYING); 407 event.setTrace(trace); 408 event.setArtifact(artifact); 409 event.setRepository(repository); 410 event.setPath(path); 411 412 dispatcher.dispatch(event.build()); 413 } 414 415 public void artifactDeployed(Artifact artifact, Path path, ArtifactTransferException exception) { 416 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_DEPLOYED); 417 event.setTrace(trace); 418 event.setArtifact(artifact); 419 event.setRepository(repository); 420 event.setPath(path); 421 event.setException(exception); 422 423 dispatcher.dispatch(event.build()); 424 } 425 426 public void metadataDeploying(Metadata metadata, Path path) { 427 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_DEPLOYING); 428 event.setTrace(trace); 429 event.setMetadata(metadata); 430 event.setRepository(repository); 431 event.setPath(path); 432 433 dispatcher.dispatch(event.build()); 434 } 435 436 public void metadataDeployed(Metadata metadata, Path path, Exception exception) { 437 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_DEPLOYED); 438 event.setTrace(trace); 439 event.setMetadata(metadata); 440 event.setRepository(repository); 441 event.setPath(path); 442 event.setException(exception); 443 444 dispatcher.dispatch(event.build()); 445 } 446 } 447 448 static final class ArtifactUploadListener extends SafeTransferListener { 449 450 private final EventCatapult catapult; 451 452 private final ArtifactUpload transfer; 453 454 ArtifactUploadListener(EventCatapult catapult, ArtifactUpload transfer) { 455 super(catapult.getSession()); 456 this.catapult = catapult; 457 this.transfer = transfer; 458 } 459 460 @Override 461 public void transferInitiated(TransferEvent event) throws TransferCancelledException { 462 super.transferInitiated(event); 463 requireNonNull(event, "event cannot be null"); 464 catapult.artifactDeploying(transfer.getArtifact(), transfer.getPath()); 465 } 466 467 @Override 468 public void transferFailed(TransferEvent event) { 469 super.transferFailed(event); 470 requireNonNull(event, "event cannot be null"); 471 catapult.artifactDeployed(transfer.getArtifact(), transfer.getPath(), transfer.getException()); 472 } 473 474 @Override 475 public void transferSucceeded(TransferEvent event) { 476 super.transferSucceeded(event); 477 requireNonNull(event, "event cannot be null"); 478 catapult.artifactDeployed(transfer.getArtifact(), transfer.getPath(), null); 479 } 480 } 481 482 static final class MetadataUploadListener extends SafeTransferListener { 483 484 private final EventCatapult catapult; 485 486 private final MetadataUpload transfer; 487 488 MetadataUploadListener(EventCatapult catapult, MetadataUpload transfer) { 489 super(catapult.getSession()); 490 this.catapult = catapult; 491 this.transfer = transfer; 492 } 493 494 @Override 495 public void transferInitiated(TransferEvent event) throws TransferCancelledException { 496 super.transferInitiated(event); 497 requireNonNull(event, "event cannot be null"); 498 catapult.metadataDeploying(transfer.getMetadata(), transfer.getPath()); 499 } 500 501 @Override 502 public void transferFailed(TransferEvent event) { 503 super.transferFailed(event); 504 requireNonNull(event, "event cannot be null"); 505 catapult.metadataDeployed(transfer.getMetadata(), transfer.getPath(), transfer.getException()); 506 } 507 508 @Override 509 public void transferSucceeded(TransferEvent event) { 510 super.transferSucceeded(event); 511 requireNonNull(event, "event cannot be null"); 512 catapult.metadataDeployed(transfer.getMetadata(), transfer.getPath(), null); 513 } 514 } 515}