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