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