1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.aether.internal.impl;
20
21 import javax.inject.Inject;
22 import javax.inject.Named;
23 import javax.inject.Singleton;
24
25 import java.io.IOException;
26 import java.nio.file.Files;
27 import java.nio.file.Path;
28 import java.util.*;
29 import java.util.ArrayList;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.IdentityHashMap;
33 import java.util.List;
34 import java.util.ListIterator;
35 import java.util.Map;
36
37 import org.eclipse.aether.RepositoryEvent;
38 import org.eclipse.aether.RepositoryEvent.EventType;
39 import org.eclipse.aether.RepositoryException;
40 import org.eclipse.aether.RepositorySystemSession;
41 import org.eclipse.aether.RequestTrace;
42 import org.eclipse.aether.SyncContext;
43 import org.eclipse.aether.artifact.Artifact;
44 import org.eclipse.aether.deployment.DeployRequest;
45 import org.eclipse.aether.deployment.DeployResult;
46 import org.eclipse.aether.deployment.DeploymentException;
47 import org.eclipse.aether.impl.Deployer;
48 import org.eclipse.aether.impl.MetadataGenerator;
49 import org.eclipse.aether.impl.MetadataGeneratorFactory;
50 import org.eclipse.aether.impl.OfflineController;
51 import org.eclipse.aether.impl.RemoteRepositoryManager;
52 import org.eclipse.aether.impl.RepositoryConnectorProvider;
53 import org.eclipse.aether.impl.RepositoryEventDispatcher;
54 import org.eclipse.aether.impl.UpdateCheck;
55 import org.eclipse.aether.impl.UpdateCheckManager;
56 import org.eclipse.aether.metadata.MergeableMetadata;
57 import org.eclipse.aether.metadata.Metadata;
58 import org.eclipse.aether.repository.LocalRepositoryManager;
59 import org.eclipse.aether.repository.RemoteRepository;
60 import org.eclipse.aether.repository.RepositoryPolicy;
61 import org.eclipse.aether.spi.artifact.generator.ArtifactGenerator;
62 import org.eclipse.aether.spi.artifact.generator.ArtifactGeneratorFactory;
63 import org.eclipse.aether.spi.connector.ArtifactUpload;
64 import org.eclipse.aether.spi.connector.MetadataDownload;
65 import org.eclipse.aether.spi.connector.MetadataUpload;
66 import org.eclipse.aether.spi.connector.RepositoryConnector;
67 import org.eclipse.aether.spi.io.PathProcessor;
68 import org.eclipse.aether.spi.synccontext.SyncContextFactory;
69 import org.eclipse.aether.transfer.ArtifactTransferException;
70 import org.eclipse.aether.transfer.MetadataNotFoundException;
71 import org.eclipse.aether.transfer.MetadataTransferException;
72 import org.eclipse.aether.transfer.NoRepositoryConnectorException;
73 import org.eclipse.aether.transfer.RepositoryOfflineException;
74 import org.eclipse.aether.transfer.TransferCancelledException;
75 import org.eclipse.aether.transfer.TransferEvent;
76 import org.slf4j.Logger;
77 import org.slf4j.LoggerFactory;
78
79 import static java.util.Objects.requireNonNull;
80
81
82
83 @Singleton
84 @Named
85 public class DefaultDeployer implements Deployer {
86 private final Logger logger = LoggerFactory.getLogger(getClass());
87
88 private final PathProcessor pathProcessor;
89
90 private final RepositoryEventDispatcher repositoryEventDispatcher;
91
92 private final RepositoryConnectorProvider repositoryConnectorProvider;
93
94 private final RemoteRepositoryManager remoteRepositoryManager;
95
96 private final UpdateCheckManager updateCheckManager;
97
98 private final Map<String, ArtifactGeneratorFactory> artifactFactories;
99
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 = getArtifactGenerators(session, request);
167 try {
168 List<Artifact> generatedArtifacts = new ArrayList<>();
169 for (ArtifactGenerator artifactGenerator : artifactGenerators) {
170 Collection<? extends Artifact> generated = artifactGenerator.generate(generatedArtifacts);
171 for (Artifact generatedArtifact : generated) {
172 Map<String, String> properties = new HashMap<>(generatedArtifact.getProperties());
173 properties.put(
174 ArtifactGeneratorFactory.ARTIFACT_GENERATOR_ID,
175 requireNonNull(artifactGenerator.generatorId(), "generatorId"));
176 Artifact ga = generatedArtifact.setProperties(properties);
177 generatedArtifacts.add(ga);
178 }
179 }
180 artifacts.addAll(generatedArtifacts);
181
182 List<? extends MetadataGenerator> generators = getMetadataGenerators(session, request);
183
184 List<ArtifactUpload> artifactUploads = new ArrayList<>();
185 List<MetadataUpload> metadataUploads = new ArrayList<>();
186 IdentityHashMap<Metadata, Object> processedMetadata = new IdentityHashMap<>();
187
188 EventCatapult catapult = new EventCatapult(session, trace, repository, repositoryEventDispatcher);
189
190 List<Metadata> metadatas = Utils.prepareMetadata(generators, artifacts);
191
192 syncContext.acquire(artifacts, Utils.combine(request.getMetadata(), metadatas));
193
194 for (Metadata metadata : metadatas) {
195 upload(metadataUploads, session, metadata, repository, connector, catapult);
196 processedMetadata.put(metadata, null);
197 }
198
199 for (ListIterator<Artifact> iterator = artifacts.listIterator(); iterator.hasNext(); ) {
200 Artifact artifact = iterator.next();
201
202 for (MetadataGenerator generator : generators) {
203 artifact = generator.transformArtifact(artifact);
204 }
205
206 iterator.set(artifact);
207
208 ArtifactUpload upload = new ArtifactUpload(artifact, artifact.getPath());
209 upload.setTrace(trace);
210 upload.setListener(new ArtifactUploadListener(catapult, upload));
211 artifactUploads.add(upload);
212 }
213
214 connector.put(artifactUploads, null);
215
216 for (ArtifactUpload upload : artifactUploads) {
217 if (upload.getException() != null) {
218 throw new DeploymentException(
219 "Failed to deploy artifacts: "
220 + upload.getException().getMessage(),
221 upload.getException());
222 }
223 if (upload.getArtifact().getProperty(ArtifactGeneratorFactory.ARTIFACT_GENERATOR_ID, null) == null) {
224 result.addArtifact(upload.getArtifact());
225 }
226 }
227
228 metadatas = Utils.finishMetadata(generators, artifacts);
229
230 syncContext.acquire(null, metadatas);
231
232 for (Metadata metadata : metadatas) {
233 upload(metadataUploads, session, metadata, repository, connector, catapult);
234 processedMetadata.put(metadata, null);
235 }
236
237 for (Metadata metadata : request.getMetadata()) {
238 if (!processedMetadata.containsKey(metadata)) {
239 upload(metadataUploads, session, metadata, repository, connector, catapult);
240 processedMetadata.put(metadata, null);
241 }
242 }
243
244 connector.put(null, metadataUploads);
245
246 for (MetadataUpload upload : metadataUploads) {
247 if (upload.getException() != null) {
248 throw new DeploymentException(
249 "Failed to deploy metadata: "
250 + upload.getException().getMessage(),
251 upload.getException());
252 }
253 result.addMetadata(upload.getMetadata());
254 }
255 } finally {
256 connector.close();
257 for (ArtifactGenerator artifactGenerator : artifactGenerators) {
258 try {
259 artifactGenerator.close();
260 } catch (Exception e) {
261 logger.warn("ArtifactGenerator close failure: {}", artifactGenerator.generatorId(), e);
262 }
263 }
264 }
265
266 return result;
267 }
268
269 private List<? extends ArtifactGenerator> getArtifactGenerators(
270 RepositorySystemSession session, DeployRequest request) {
271 PrioritizedComponents<ArtifactGeneratorFactory> factories =
272 Utils.sortArtifactGeneratorFactories(session, artifactFactories);
273
274 List<ArtifactGenerator> generators = new ArrayList<>();
275
276 for (PrioritizedComponent<ArtifactGeneratorFactory> factory : factories.getEnabled()) {
277 ArtifactGenerator generator = factory.getComponent().newInstance(session, request);
278 if (generator != null) {
279 generators.add(generator);
280 }
281 }
282
283 return generators;
284 }
285
286 private List<? extends MetadataGenerator> getMetadataGenerators(
287 RepositorySystemSession session, DeployRequest request) {
288 PrioritizedComponents<MetadataGeneratorFactory> factories =
289 Utils.sortMetadataGeneratorFactories(session, metadataFactories);
290
291 List<MetadataGenerator> generators = new ArrayList<>();
292
293 for (PrioritizedComponent<MetadataGeneratorFactory> factory : factories.getEnabled()) {
294 MetadataGenerator generator = factory.getComponent().newInstance(session, request);
295 if (generator != null) {
296 generators.add(generator);
297 }
298 }
299
300 return generators;
301 }
302
303 private void upload(
304 Collection<MetadataUpload> metadataUploads,
305 RepositorySystemSession session,
306 Metadata metadata,
307 RemoteRepository repository,
308 RepositoryConnector connector,
309 EventCatapult catapult)
310 throws DeploymentException {
311 LocalRepositoryManager lrm = session.getLocalRepositoryManager();
312 Path basePath = lrm.getRepository().getBasePath();
313
314 Path dstPath = basePath.resolve(lrm.getPathForRemoteMetadata(metadata, repository, ""));
315
316 if (metadata instanceof MergeableMetadata) {
317 if (!((MergeableMetadata) metadata).isMerged()) {
318 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_RESOLVING);
319 event.setTrace(catapult.getTrace());
320 event.setMetadata(metadata);
321 event.setRepository(repository);
322 repositoryEventDispatcher.dispatch(event.build());
323
324 event = new RepositoryEvent.Builder(session, EventType.METADATA_DOWNLOADING);
325 event.setTrace(catapult.getTrace());
326 event.setMetadata(metadata);
327 event.setRepository(repository);
328 repositoryEventDispatcher.dispatch(event.build());
329
330 RepositoryPolicy policy = getPolicy(session, repository, metadata.getNature());
331 MetadataDownload download = new MetadataDownload();
332 download.setMetadata(metadata);
333 download.setPath(dstPath);
334 download.setChecksumPolicy(policy.getChecksumPolicy());
335 download.setListener(SafeTransferListener.wrap(session));
336 download.setTrace(catapult.getTrace());
337 connector.get(null, Collections.singletonList(download));
338
339 Exception error = download.getException();
340
341 if (error instanceof MetadataNotFoundException) {
342 try {
343 Files.deleteIfExists(dstPath);
344 } catch (IOException e) {
345 throw new DeploymentException(
346 "Failed to delete cached metadata " + metadata + ": " + e.getMessage(), e);
347 }
348 }
349
350 event = new RepositoryEvent.Builder(session, EventType.METADATA_DOWNLOADED);
351 event.setTrace(catapult.getTrace());
352 event.setMetadata(metadata);
353 event.setRepository(repository);
354 event.setException(error);
355 event.setPath(dstPath);
356 repositoryEventDispatcher.dispatch(event.build());
357
358 event = new RepositoryEvent.Builder(session, EventType.METADATA_RESOLVED);
359 event.setTrace(catapult.getTrace());
360 event.setMetadata(metadata);
361 event.setRepository(repository);
362 event.setException(error);
363 event.setPath(dstPath);
364 repositoryEventDispatcher.dispatch(event.build());
365
366 if (error != null && !(error instanceof MetadataNotFoundException)) {
367 throw new DeploymentException(
368 "Failed to retrieve remote metadata " + metadata + ": " + error.getMessage(), error);
369 }
370 }
371
372 try {
373 ((MergeableMetadata) metadata).merge(dstPath, dstPath);
374 } catch (RepositoryException e) {
375 throw new DeploymentException("Failed to update metadata " + metadata + ": " + e.getMessage(), e);
376 }
377 } else {
378 if (metadata.getPath() == null) {
379 throw new DeploymentException("Failed to update metadata " + metadata + ": No file attached.");
380 }
381 try {
382 pathProcessor.copy(metadata.getPath(), dstPath);
383 } catch (IOException e) {
384 throw new DeploymentException("Failed to update metadata " + metadata + ": " + e.getMessage(), e);
385 }
386 }
387
388 UpdateCheck<Metadata, MetadataTransferException> check = new UpdateCheck<>();
389 check.setItem(metadata);
390 check.setPath(dstPath);
391 check.setRepository(repository);
392 check.setAuthoritativeRepository(repository);
393 updateCheckManager.touchMetadata(session, check);
394
395 MetadataUpload upload = new MetadataUpload(metadata, dstPath);
396 upload.setTrace(catapult.getTrace());
397 upload.setListener(new MetadataUploadListener(catapult, upload));
398 metadataUploads.add(upload);
399 }
400
401 private RepositoryPolicy getPolicy(
402 RepositorySystemSession session, RemoteRepository repository, Metadata.Nature nature) {
403 boolean releases = !Metadata.Nature.SNAPSHOT.equals(nature);
404 boolean snapshots = !Metadata.Nature.RELEASE.equals(nature);
405 return remoteRepositoryManager.getPolicy(session, repository, releases, snapshots);
406 }
407
408 static final class EventCatapult {
409
410 private final RepositorySystemSession session;
411
412 private final RequestTrace trace;
413
414 private final RemoteRepository repository;
415
416 private final RepositoryEventDispatcher dispatcher;
417
418 EventCatapult(
419 RepositorySystemSession session,
420 RequestTrace trace,
421 RemoteRepository repository,
422 RepositoryEventDispatcher dispatcher) {
423 this.session = session;
424 this.trace = trace;
425 this.repository = repository;
426 this.dispatcher = dispatcher;
427 }
428
429 public RepositorySystemSession getSession() {
430 return session;
431 }
432
433 public RequestTrace getTrace() {
434 return trace;
435 }
436
437 public void artifactDeploying(Artifact artifact, Path path) {
438 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_DEPLOYING);
439 event.setTrace(trace);
440 event.setArtifact(artifact);
441 event.setRepository(repository);
442 event.setPath(path);
443
444 dispatcher.dispatch(event.build());
445 }
446
447 public void artifactDeployed(Artifact artifact, Path path, ArtifactTransferException exception) {
448 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_DEPLOYED);
449 event.setTrace(trace);
450 event.setArtifact(artifact);
451 event.setRepository(repository);
452 event.setPath(path);
453 event.setException(exception);
454
455 dispatcher.dispatch(event.build());
456 }
457
458 public void metadataDeploying(Metadata metadata, Path path) {
459 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_DEPLOYING);
460 event.setTrace(trace);
461 event.setMetadata(metadata);
462 event.setRepository(repository);
463 event.setPath(path);
464
465 dispatcher.dispatch(event.build());
466 }
467
468 public void metadataDeployed(Metadata metadata, Path path, Exception exception) {
469 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_DEPLOYED);
470 event.setTrace(trace);
471 event.setMetadata(metadata);
472 event.setRepository(repository);
473 event.setPath(path);
474 event.setException(exception);
475
476 dispatcher.dispatch(event.build());
477 }
478 }
479
480 static final class ArtifactUploadListener extends SafeTransferListener {
481
482 private final EventCatapult catapult;
483
484 private final ArtifactUpload transfer;
485
486 ArtifactUploadListener(EventCatapult catapult, ArtifactUpload transfer) {
487 super(catapult.getSession());
488 this.catapult = catapult;
489 this.transfer = transfer;
490 }
491
492 @Override
493 public void transferInitiated(TransferEvent event) throws TransferCancelledException {
494 super.transferInitiated(event);
495 requireNonNull(event, "event cannot be null");
496 catapult.artifactDeploying(transfer.getArtifact(), transfer.getPath());
497 }
498
499 @Override
500 public void transferFailed(TransferEvent event) {
501 super.transferFailed(event);
502 requireNonNull(event, "event cannot be null");
503 catapult.artifactDeployed(transfer.getArtifact(), transfer.getPath(), transfer.getException());
504 }
505
506 @Override
507 public void transferSucceeded(TransferEvent event) {
508 super.transferSucceeded(event);
509 requireNonNull(event, "event cannot be null");
510 catapult.artifactDeployed(transfer.getArtifact(), transfer.getPath(), null);
511 }
512 }
513
514 static final class MetadataUploadListener extends SafeTransferListener {
515
516 private final EventCatapult catapult;
517
518 private final MetadataUpload transfer;
519
520 MetadataUploadListener(EventCatapult catapult, MetadataUpload transfer) {
521 super(catapult.getSession());
522 this.catapult = catapult;
523 this.transfer = transfer;
524 }
525
526 @Override
527 public void transferInitiated(TransferEvent event) throws TransferCancelledException {
528 super.transferInitiated(event);
529 requireNonNull(event, "event cannot be null");
530 catapult.metadataDeploying(transfer.getMetadata(), transfer.getPath());
531 }
532
533 @Override
534 public void transferFailed(TransferEvent event) {
535 super.transferFailed(event);
536 requireNonNull(event, "event cannot be null");
537 catapult.metadataDeployed(transfer.getMetadata(), transfer.getPath(), transfer.getException());
538 }
539
540 @Override
541 public void transferSucceeded(TransferEvent event) {
542 super.transferSucceeded(event);
543 requireNonNull(event, "event cannot be null");
544 catapult.metadataDeployed(transfer.getMetadata(), transfer.getPath(), null);
545 }
546 }
547 }