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.File;
26  import java.io.IOException;
27  import java.util.ArrayList;
28  import java.util.Collection;
29  import java.util.Collections;
30  import java.util.IdentityHashMap;
31  import java.util.List;
32  import java.util.ListIterator;
33  import java.util.Set;
34  
35  import org.eclipse.aether.RepositoryEvent;
36  import org.eclipse.aether.RepositoryEvent.EventType;
37  import org.eclipse.aether.RepositoryException;
38  import org.eclipse.aether.RepositorySystemSession;
39  import org.eclipse.aether.RequestTrace;
40  import org.eclipse.aether.SyncContext;
41  import org.eclipse.aether.artifact.Artifact;
42  import org.eclipse.aether.deployment.DeployRequest;
43  import org.eclipse.aether.deployment.DeployResult;
44  import org.eclipse.aether.deployment.DeploymentException;
45  import org.eclipse.aether.impl.Deployer;
46  import org.eclipse.aether.impl.MetadataGenerator;
47  import org.eclipse.aether.impl.MetadataGeneratorFactory;
48  import org.eclipse.aether.impl.OfflineController;
49  import org.eclipse.aether.impl.RemoteRepositoryManager;
50  import org.eclipse.aether.impl.RepositoryConnectorProvider;
51  import org.eclipse.aether.impl.RepositoryEventDispatcher;
52  import org.eclipse.aether.impl.UpdateCheck;
53  import org.eclipse.aether.impl.UpdateCheckManager;
54  import org.eclipse.aether.metadata.MergeableMetadata;
55  import org.eclipse.aether.metadata.Metadata;
56  import org.eclipse.aether.repository.LocalRepositoryManager;
57  import org.eclipse.aether.repository.RemoteRepository;
58  import org.eclipse.aether.repository.RepositoryPolicy;
59  import org.eclipse.aether.spi.connector.ArtifactUpload;
60  import org.eclipse.aether.spi.connector.MetadataDownload;
61  import org.eclipse.aether.spi.connector.MetadataUpload;
62  import org.eclipse.aether.spi.connector.RepositoryConnector;
63  import org.eclipse.aether.spi.io.FileProcessor;
64  import org.eclipse.aether.spi.locator.Service;
65  import org.eclipse.aether.spi.locator.ServiceLocator;
66  import org.eclipse.aether.spi.synccontext.SyncContextFactory;
67  import org.eclipse.aether.transfer.ArtifactTransferException;
68  import org.eclipse.aether.transfer.MetadataNotFoundException;
69  import org.eclipse.aether.transfer.MetadataTransferException;
70  import org.eclipse.aether.transfer.NoRepositoryConnectorException;
71  import org.eclipse.aether.transfer.RepositoryOfflineException;
72  import org.eclipse.aether.transfer.TransferCancelledException;
73  import org.eclipse.aether.transfer.TransferEvent;
74  import org.eclipse.aether.transform.FileTransformer;
75  import org.eclipse.aether.transform.FileTransformerManager;
76  
77  import static java.util.Objects.requireNonNull;
78  
79  /**
80   */
81  @Singleton
82  @Named
83  public class DefaultDeployer implements Deployer, Service {
84      private FileProcessor fileProcessor;
85  
86      private RepositoryEventDispatcher repositoryEventDispatcher;
87  
88      private RepositoryConnectorProvider repositoryConnectorProvider;
89  
90      private RemoteRepositoryManager remoteRepositoryManager;
91  
92      private UpdateCheckManager updateCheckManager;
93  
94      private Collection<MetadataGeneratorFactory> metadataFactories = new ArrayList<>();
95  
96      private SyncContextFactory syncContextFactory;
97  
98      private OfflineController offlineController;
99  
100     public DefaultDeployer() {
101         // enables default constructor
102     }
103 
104     @SuppressWarnings("checkstyle:parameternumber")
105     @Inject
106     DefaultDeployer(
107             FileProcessor fileProcessor,
108             RepositoryEventDispatcher repositoryEventDispatcher,
109             RepositoryConnectorProvider repositoryConnectorProvider,
110             RemoteRepositoryManager remoteRepositoryManager,
111             UpdateCheckManager updateCheckManager,
112             Set<MetadataGeneratorFactory> metadataFactories,
113             SyncContextFactory syncContextFactory,
114             OfflineController offlineController) {
115         setFileProcessor(fileProcessor);
116         setRepositoryEventDispatcher(repositoryEventDispatcher);
117         setRepositoryConnectorProvider(repositoryConnectorProvider);
118         setRemoteRepositoryManager(remoteRepositoryManager);
119         setUpdateCheckManager(updateCheckManager);
120         setMetadataGeneratorFactories(metadataFactories);
121         setSyncContextFactory(syncContextFactory);
122         setOfflineController(offlineController);
123     }
124 
125     public void initService(ServiceLocator locator) {
126         setFileProcessor(locator.getService(FileProcessor.class));
127         setRepositoryEventDispatcher(locator.getService(RepositoryEventDispatcher.class));
128         setRepositoryConnectorProvider(locator.getService(RepositoryConnectorProvider.class));
129         setRemoteRepositoryManager(locator.getService(RemoteRepositoryManager.class));
130         setUpdateCheckManager(locator.getService(UpdateCheckManager.class));
131         setMetadataGeneratorFactories(locator.getServices(MetadataGeneratorFactory.class));
132         setSyncContextFactory(locator.getService(SyncContextFactory.class));
133         setOfflineController(locator.getService(OfflineController.class));
134     }
135 
136     public DefaultDeployer setFileProcessor(FileProcessor fileProcessor) {
137         this.fileProcessor = requireNonNull(fileProcessor, "file processor cannot be null");
138         return this;
139     }
140 
141     public DefaultDeployer setRepositoryEventDispatcher(RepositoryEventDispatcher repositoryEventDispatcher) {
142         this.repositoryEventDispatcher =
143                 requireNonNull(repositoryEventDispatcher, "repository event dispatcher cannot be null");
144         return this;
145     }
146 
147     public DefaultDeployer setRepositoryConnectorProvider(RepositoryConnectorProvider repositoryConnectorProvider) {
148         this.repositoryConnectorProvider =
149                 requireNonNull(repositoryConnectorProvider, "repository connector provider cannot be null");
150         return this;
151     }
152 
153     public DefaultDeployer setRemoteRepositoryManager(RemoteRepositoryManager remoteRepositoryManager) {
154         this.remoteRepositoryManager =
155                 requireNonNull(remoteRepositoryManager, "remote repository provider cannot be null");
156         return this;
157     }
158 
159     public DefaultDeployer setUpdateCheckManager(UpdateCheckManager updateCheckManager) {
160         this.updateCheckManager = requireNonNull(updateCheckManager, "update check manager cannot be null");
161         return this;
162     }
163 
164     public DefaultDeployer addMetadataGeneratorFactory(MetadataGeneratorFactory factory) {
165         metadataFactories.add(requireNonNull(factory, "metadata generator factory cannot be null"));
166         return this;
167     }
168 
169     public DefaultDeployer setMetadataGeneratorFactories(Collection<MetadataGeneratorFactory> metadataFactories) {
170         if (metadataFactories == null) {
171             this.metadataFactories = new ArrayList<>();
172         } else {
173             this.metadataFactories = metadataFactories;
174         }
175         return this;
176     }
177 
178     public DefaultDeployer setSyncContextFactory(SyncContextFactory syncContextFactory) {
179         this.syncContextFactory = requireNonNull(syncContextFactory, "sync context factory cannot be null");
180         return this;
181     }
182 
183     public DefaultDeployer setOfflineController(OfflineController offlineController) {
184         this.offlineController = requireNonNull(offlineController, "offline controller cannot be null");
185         return this;
186     }
187 
188     public DeployResult deploy(RepositorySystemSession session, DeployRequest request) throws DeploymentException {
189         requireNonNull(session, "session cannot be null");
190         requireNonNull(request, "request cannot be null");
191         try {
192             Utils.checkOffline(session, offlineController, request.getRepository());
193         } catch (RepositoryOfflineException e) {
194             throw new DeploymentException(
195                     "Cannot deploy while " + request.getRepository().getId() + " ("
196                             + request.getRepository().getUrl() + ") is in offline mode",
197                     e);
198         }
199 
200         try (SyncContext syncContext = syncContextFactory.newInstance(session, false)) {
201             return deploy(syncContext, session, request);
202         }
203     }
204 
205     private DeployResult deploy(SyncContext syncContext, RepositorySystemSession session, DeployRequest request)
206             throws DeploymentException {
207         DeployResult result = new DeployResult(request);
208 
209         RequestTrace trace = RequestTrace.newChild(request.getTrace(), request);
210 
211         RemoteRepository repository = request.getRepository();
212 
213         RepositoryConnector connector;
214         try {
215             connector = repositoryConnectorProvider.newRepositoryConnector(session, repository);
216         } catch (NoRepositoryConnectorException e) {
217             throw new DeploymentException("Failed to deploy artifacts/metadata: " + e.getMessage(), e);
218         }
219 
220         try {
221             List<? extends MetadataGenerator> generators = getMetadataGenerators(session, request);
222 
223             FileTransformerManager fileTransformerManager = session.getFileTransformerManager();
224 
225             List<ArtifactUpload> artifactUploads = new ArrayList<>();
226             List<MetadataUpload> metadataUploads = new ArrayList<>();
227             IdentityHashMap<Metadata, Object> processedMetadata = new IdentityHashMap<>();
228 
229             EventCatapult catapult = new EventCatapult(session, trace, repository, repositoryEventDispatcher);
230 
231             List<Artifact> artifacts = new ArrayList<>(request.getArtifacts());
232 
233             List<Metadata> metadatas = Utils.prepareMetadata(generators, artifacts);
234 
235             syncContext.acquire(artifacts, Utils.combine(request.getMetadata(), metadatas));
236 
237             for (Metadata metadata : metadatas) {
238                 upload(metadataUploads, session, metadata, repository, connector, catapult);
239                 processedMetadata.put(metadata, null);
240             }
241 
242             for (ListIterator<Artifact> iterator = artifacts.listIterator(); iterator.hasNext(); ) {
243                 Artifact artifact = iterator.next();
244 
245                 for (MetadataGenerator generator : generators) {
246                     artifact = generator.transformArtifact(artifact);
247                 }
248 
249                 iterator.set(artifact);
250 
251                 Collection<FileTransformer> fileTransformers =
252                         fileTransformerManager.getTransformersForArtifact(artifact);
253                 if (!fileTransformers.isEmpty()) {
254                     for (FileTransformer fileTransformer : fileTransformers) {
255                         Artifact targetArtifact = fileTransformer.transformArtifact(artifact);
256 
257                         ArtifactUpload upload = new ArtifactUpload(targetArtifact, artifact.getFile(), fileTransformer);
258                         upload.setTrace(trace);
259                         upload.setListener(new ArtifactUploadListener(catapult, upload));
260                         artifactUploads.add(upload);
261                     }
262                 } else {
263                     ArtifactUpload upload = new ArtifactUpload(artifact, artifact.getFile());
264                     upload.setTrace(trace);
265                     upload.setListener(new ArtifactUploadListener(catapult, upload));
266                     artifactUploads.add(upload);
267                 }
268             }
269 
270             connector.put(artifactUploads, null);
271 
272             for (ArtifactUpload upload : artifactUploads) {
273                 if (upload.getException() != null) {
274                     throw new DeploymentException(
275                             "Failed to deploy artifacts: "
276                                     + upload.getException().getMessage(),
277                             upload.getException());
278                 }
279                 result.addArtifact(upload.getArtifact());
280             }
281 
282             metadatas = Utils.finishMetadata(generators, artifacts);
283 
284             syncContext.acquire(null, metadatas);
285 
286             for (Metadata metadata : metadatas) {
287                 upload(metadataUploads, session, metadata, repository, connector, catapult);
288                 processedMetadata.put(metadata, null);
289             }
290 
291             for (Metadata metadata : request.getMetadata()) {
292                 if (!processedMetadata.containsKey(metadata)) {
293                     upload(metadataUploads, session, metadata, repository, connector, catapult);
294                     processedMetadata.put(metadata, null);
295                 }
296             }
297 
298             connector.put(null, metadataUploads);
299 
300             for (MetadataUpload upload : metadataUploads) {
301                 if (upload.getException() != null) {
302                     throw new DeploymentException(
303                             "Failed to deploy metadata: "
304                                     + upload.getException().getMessage(),
305                             upload.getException());
306                 }
307                 result.addMetadata(upload.getMetadata());
308             }
309         } finally {
310             connector.close();
311         }
312 
313         return result;
314     }
315 
316     private List<? extends MetadataGenerator> getMetadataGenerators(
317             RepositorySystemSession session, DeployRequest request) {
318         PrioritizedComponents<MetadataGeneratorFactory> factories =
319                 Utils.sortMetadataGeneratorFactories(session, this.metadataFactories);
320 
321         List<MetadataGenerator> generators = new ArrayList<>();
322 
323         for (PrioritizedComponent<MetadataGeneratorFactory> factory : factories.getEnabled()) {
324             MetadataGenerator generator = factory.getComponent().newInstance(session, request);
325             if (generator != null) {
326                 generators.add(generator);
327             }
328         }
329 
330         return generators;
331     }
332 
333     private void upload(
334             Collection<MetadataUpload> metadataUploads,
335             RepositorySystemSession session,
336             Metadata metadata,
337             RemoteRepository repository,
338             RepositoryConnector connector,
339             EventCatapult catapult)
340             throws DeploymentException {
341         LocalRepositoryManager lrm = session.getLocalRepositoryManager();
342         File basedir = lrm.getRepository().getBasedir();
343 
344         File dstFile = new File(basedir, lrm.getPathForRemoteMetadata(metadata, repository, ""));
345 
346         if (metadata instanceof MergeableMetadata) {
347             if (!((MergeableMetadata) metadata).isMerged()) {
348                 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_RESOLVING);
349                 event.setTrace(catapult.getTrace());
350                 event.setMetadata(metadata);
351                 event.setRepository(repository);
352                 repositoryEventDispatcher.dispatch(event.build());
353 
354                 event = new RepositoryEvent.Builder(session, EventType.METADATA_DOWNLOADING);
355                 event.setTrace(catapult.getTrace());
356                 event.setMetadata(metadata);
357                 event.setRepository(repository);
358                 repositoryEventDispatcher.dispatch(event.build());
359 
360                 RepositoryPolicy policy = getPolicy(session, repository, metadata.getNature());
361                 MetadataDownload download = new MetadataDownload();
362                 download.setMetadata(metadata);
363                 download.setFile(dstFile);
364                 download.setChecksumPolicy(policy.getChecksumPolicy());
365                 download.setListener(SafeTransferListener.wrap(session));
366                 download.setTrace(catapult.getTrace());
367                 connector.get(null, Collections.singletonList(download));
368 
369                 Exception error = download.getException();
370 
371                 if (error instanceof MetadataNotFoundException) {
372                     dstFile.delete();
373                 }
374 
375                 event = new RepositoryEvent.Builder(session, EventType.METADATA_DOWNLOADED);
376                 event.setTrace(catapult.getTrace());
377                 event.setMetadata(metadata);
378                 event.setRepository(repository);
379                 event.setException(error);
380                 event.setFile(dstFile);
381                 repositoryEventDispatcher.dispatch(event.build());
382 
383                 event = new RepositoryEvent.Builder(session, EventType.METADATA_RESOLVED);
384                 event.setTrace(catapult.getTrace());
385                 event.setMetadata(metadata);
386                 event.setRepository(repository);
387                 event.setException(error);
388                 event.setFile(dstFile);
389                 repositoryEventDispatcher.dispatch(event.build());
390 
391                 if (error != null && !(error instanceof MetadataNotFoundException)) {
392                     throw new DeploymentException(
393                             "Failed to retrieve remote metadata " + metadata + ": " + error.getMessage(), error);
394                 }
395             }
396 
397             try {
398                 ((MergeableMetadata) metadata).merge(dstFile, dstFile);
399             } catch (RepositoryException e) {
400                 throw new DeploymentException("Failed to update metadata " + metadata + ": " + e.getMessage(), e);
401             }
402         } else {
403             if (metadata.getFile() == null) {
404                 throw new DeploymentException("Failed to update metadata " + metadata + ": No file attached.");
405             }
406             try {
407                 fileProcessor.copy(metadata.getFile(), dstFile);
408             } catch (IOException e) {
409                 throw new DeploymentException("Failed to update metadata " + metadata + ": " + e.getMessage(), e);
410             }
411         }
412 
413         UpdateCheck<Metadata, MetadataTransferException> check = new UpdateCheck<>();
414         check.setItem(metadata);
415         check.setFile(dstFile);
416         check.setRepository(repository);
417         check.setAuthoritativeRepository(repository);
418         updateCheckManager.touchMetadata(session, check);
419 
420         MetadataUpload upload = new MetadataUpload(metadata, dstFile);
421         upload.setTrace(catapult.getTrace());
422         upload.setListener(new MetadataUploadListener(catapult, upload));
423         metadataUploads.add(upload);
424     }
425 
426     private RepositoryPolicy getPolicy(
427             RepositorySystemSession session, RemoteRepository repository, Metadata.Nature nature) {
428         boolean releases = !Metadata.Nature.SNAPSHOT.equals(nature);
429         boolean snapshots = !Metadata.Nature.RELEASE.equals(nature);
430         return remoteRepositoryManager.getPolicy(session, repository, releases, snapshots);
431     }
432 
433     static final class EventCatapult {
434 
435         private final RepositorySystemSession session;
436 
437         private final RequestTrace trace;
438 
439         private final RemoteRepository repository;
440 
441         private final RepositoryEventDispatcher dispatcher;
442 
443         EventCatapult(
444                 RepositorySystemSession session,
445                 RequestTrace trace,
446                 RemoteRepository repository,
447                 RepositoryEventDispatcher dispatcher) {
448             this.session = session;
449             this.trace = trace;
450             this.repository = repository;
451             this.dispatcher = dispatcher;
452         }
453 
454         public RepositorySystemSession getSession() {
455             return session;
456         }
457 
458         public RequestTrace getTrace() {
459             return trace;
460         }
461 
462         public void artifactDeploying(Artifact artifact, File file) {
463             RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_DEPLOYING);
464             event.setTrace(trace);
465             event.setArtifact(artifact);
466             event.setRepository(repository);
467             event.setFile(file);
468 
469             dispatcher.dispatch(event.build());
470         }
471 
472         public void artifactDeployed(Artifact artifact, File file, ArtifactTransferException exception) {
473             RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_DEPLOYED);
474             event.setTrace(trace);
475             event.setArtifact(artifact);
476             event.setRepository(repository);
477             event.setFile(file);
478             event.setException(exception);
479 
480             dispatcher.dispatch(event.build());
481         }
482 
483         public void metadataDeploying(Metadata metadata, File file) {
484             RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_DEPLOYING);
485             event.setTrace(trace);
486             event.setMetadata(metadata);
487             event.setRepository(repository);
488             event.setFile(file);
489 
490             dispatcher.dispatch(event.build());
491         }
492 
493         public void metadataDeployed(Metadata metadata, File file, Exception exception) {
494             RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_DEPLOYED);
495             event.setTrace(trace);
496             event.setMetadata(metadata);
497             event.setRepository(repository);
498             event.setFile(file);
499             event.setException(exception);
500 
501             dispatcher.dispatch(event.build());
502         }
503     }
504 
505     static final class ArtifactUploadListener extends SafeTransferListener {
506 
507         private final EventCatapult catapult;
508 
509         private final ArtifactUpload transfer;
510 
511         ArtifactUploadListener(EventCatapult catapult, ArtifactUpload transfer) {
512             super(catapult.getSession());
513             this.catapult = catapult;
514             this.transfer = transfer;
515         }
516 
517         @Override
518         public void transferInitiated(TransferEvent event) throws TransferCancelledException {
519             super.transferInitiated(event);
520             requireNonNull(event, "event cannot be null");
521             catapult.artifactDeploying(transfer.getArtifact(), transfer.getFile());
522         }
523 
524         @Override
525         public void transferFailed(TransferEvent event) {
526             super.transferFailed(event);
527             requireNonNull(event, "event cannot be null");
528             catapult.artifactDeployed(transfer.getArtifact(), transfer.getFile(), transfer.getException());
529         }
530 
531         @Override
532         public void transferSucceeded(TransferEvent event) {
533             super.transferSucceeded(event);
534             requireNonNull(event, "event cannot be null");
535             catapult.artifactDeployed(transfer.getArtifact(), transfer.getFile(), null);
536         }
537     }
538 
539     static final class MetadataUploadListener extends SafeTransferListener {
540 
541         private final EventCatapult catapult;
542 
543         private final MetadataUpload transfer;
544 
545         MetadataUploadListener(EventCatapult catapult, MetadataUpload transfer) {
546             super(catapult.getSession());
547             this.catapult = catapult;
548             this.transfer = transfer;
549         }
550 
551         @Override
552         public void transferInitiated(TransferEvent event) throws TransferCancelledException {
553             super.transferInitiated(event);
554             requireNonNull(event, "event cannot be null");
555             catapult.metadataDeploying(transfer.getMetadata(), transfer.getFile());
556         }
557 
558         @Override
559         public void transferFailed(TransferEvent event) {
560             super.transferFailed(event);
561             requireNonNull(event, "event cannot be null");
562             catapult.metadataDeployed(transfer.getMetadata(), transfer.getFile(), transfer.getException());
563         }
564 
565         @Override
566         public void transferSucceeded(TransferEvent event) {
567             super.transferSucceeded(event);
568             requireNonNull(event, "event cannot be null");
569             catapult.metadataDeployed(transfer.getMetadata(), transfer.getFile(), null);
570         }
571     }
572 }