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