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 = 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 }