View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
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 }