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.ArrayList;
29 import java.util.Collection;
30 import java.util.Collections;
31 import java.util.IdentityHashMap;
32 import java.util.List;
33 import java.util.ListIterator;
34 import java.util.Map;
35
36 import org.eclipse.aether.RepositoryEvent;
37 import org.eclipse.aether.RepositoryEvent.EventType;
38 import org.eclipse.aether.RepositoryException;
39 import org.eclipse.aether.RepositorySystemSession;
40 import org.eclipse.aether.RequestTrace;
41 import org.eclipse.aether.SyncContext;
42 import org.eclipse.aether.artifact.Artifact;
43 import org.eclipse.aether.deployment.DeployRequest;
44 import org.eclipse.aether.deployment.DeployResult;
45 import org.eclipse.aether.deployment.DeploymentException;
46 import org.eclipse.aether.impl.Deployer;
47 import org.eclipse.aether.impl.MetadataGenerator;
48 import org.eclipse.aether.impl.MetadataGeneratorFactory;
49 import org.eclipse.aether.impl.OfflineController;
50 import org.eclipse.aether.impl.RemoteRepositoryManager;
51 import org.eclipse.aether.impl.RepositoryConnectorProvider;
52 import org.eclipse.aether.impl.RepositoryEventDispatcher;
53 import org.eclipse.aether.impl.UpdateCheck;
54 import org.eclipse.aether.impl.UpdateCheckManager;
55 import org.eclipse.aether.metadata.MergeableMetadata;
56 import org.eclipse.aether.metadata.Metadata;
57 import org.eclipse.aether.repository.LocalRepositoryManager;
58 import org.eclipse.aether.repository.RemoteRepository;
59 import org.eclipse.aether.repository.RepositoryPolicy;
60 import org.eclipse.aether.spi.connector.ArtifactUpload;
61 import org.eclipse.aether.spi.connector.MetadataDownload;
62 import org.eclipse.aether.spi.connector.MetadataUpload;
63 import org.eclipse.aether.spi.connector.RepositoryConnector;
64 import org.eclipse.aether.spi.io.PathProcessor;
65 import org.eclipse.aether.spi.synccontext.SyncContextFactory;
66 import org.eclipse.aether.transfer.ArtifactTransferException;
67 import org.eclipse.aether.transfer.MetadataNotFoundException;
68 import org.eclipse.aether.transfer.MetadataTransferException;
69 import org.eclipse.aether.transfer.NoRepositoryConnectorException;
70 import org.eclipse.aether.transfer.RepositoryOfflineException;
71 import org.eclipse.aether.transfer.TransferCancelledException;
72 import org.eclipse.aether.transfer.TransferEvent;
73
74 import static java.util.Objects.requireNonNull;
75
76
77
78 @Singleton
79 @Named
80 public class DefaultDeployer implements Deployer {
81 private final PathProcessor pathProcessor;
82
83 private final RepositoryEventDispatcher repositoryEventDispatcher;
84
85 private final RepositoryConnectorProvider repositoryConnectorProvider;
86
87 private final RemoteRepositoryManager remoteRepositoryManager;
88
89 private final UpdateCheckManager updateCheckManager;
90
91 private final Map<String, MetadataGeneratorFactory> metadataFactories;
92
93 private final SyncContextFactory syncContextFactory;
94
95 private final OfflineController offlineController;
96
97 @SuppressWarnings("checkstyle:parameternumber")
98 @Inject
99 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 }