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