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.*;
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 =
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 }