001package org.eclipse.aether.internal.impl;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 * 
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 * 
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.File;
023import java.io.IOException;
024import java.util.ArrayList;
025import java.util.Arrays;
026import java.util.Collection;
027import java.util.IdentityHashMap;
028import java.util.List;
029import static java.util.Objects.requireNonNull;
030import java.util.Set;
031
032import javax.inject.Inject;
033import javax.inject.Named;
034
035import org.eclipse.aether.RepositoryEvent;
036import org.eclipse.aether.RepositoryEvent.EventType;
037import org.eclipse.aether.RepositoryException;
038import org.eclipse.aether.RepositorySystemSession;
039import org.eclipse.aether.RequestTrace;
040import org.eclipse.aether.SyncContext;
041import org.eclipse.aether.artifact.Artifact;
042import org.eclipse.aether.deployment.DeployRequest;
043import org.eclipse.aether.deployment.DeployResult;
044import org.eclipse.aether.deployment.DeploymentException;
045import org.eclipse.aether.impl.Deployer;
046import org.eclipse.aether.impl.MetadataGenerator;
047import org.eclipse.aether.impl.MetadataGeneratorFactory;
048import org.eclipse.aether.impl.OfflineController;
049import org.eclipse.aether.impl.RemoteRepositoryManager;
050import org.eclipse.aether.impl.RepositoryConnectorProvider;
051import org.eclipse.aether.impl.RepositoryEventDispatcher;
052import org.eclipse.aether.impl.SyncContextFactory;
053import org.eclipse.aether.impl.UpdateCheck;
054import org.eclipse.aether.impl.UpdateCheckManager;
055import org.eclipse.aether.metadata.MergeableMetadata;
056import org.eclipse.aether.metadata.Metadata;
057import org.eclipse.aether.repository.LocalRepositoryManager;
058import org.eclipse.aether.repository.RemoteRepository;
059import org.eclipse.aether.repository.RepositoryPolicy;
060import org.eclipse.aether.spi.connector.ArtifactUpload;
061import org.eclipse.aether.spi.connector.MetadataDownload;
062import org.eclipse.aether.spi.connector.MetadataUpload;
063import org.eclipse.aether.spi.connector.RepositoryConnector;
064import org.eclipse.aether.spi.io.FileProcessor;
065import org.eclipse.aether.spi.locator.Service;
066import org.eclipse.aether.spi.locator.ServiceLocator;
067import org.eclipse.aether.transfer.ArtifactTransferException;
068import org.eclipse.aether.transfer.MetadataNotFoundException;
069import org.eclipse.aether.transfer.MetadataTransferException;
070import org.eclipse.aether.transfer.NoRepositoryConnectorException;
071import org.eclipse.aether.transfer.RepositoryOfflineException;
072import org.eclipse.aether.transfer.TransferCancelledException;
073import org.eclipse.aether.transfer.TransferEvent;
074import org.eclipse.aether.transform.FileTransformer;
075import org.eclipse.aether.transform.FileTransformerManager;
076
077/**
078 */
079@Named
080public class DefaultDeployer
081    implements Deployer, Service
082{
083    private FileProcessor fileProcessor;
084
085    private RepositoryEventDispatcher repositoryEventDispatcher;
086
087    private RepositoryConnectorProvider repositoryConnectorProvider;
088
089    private RemoteRepositoryManager remoteRepositoryManager;
090
091    private UpdateCheckManager updateCheckManager;
092
093    private Collection<MetadataGeneratorFactory> metadataFactories = new ArrayList<>();
094
095    private SyncContextFactory syncContextFactory;
096
097    private OfflineController offlineController;
098
099    public DefaultDeployer()
100    {
101        // enables default constructor
102    }
103
104    @SuppressWarnings( "checkstyle:parameternumber" )
105    @Inject
106    DefaultDeployer( FileProcessor fileProcessor, RepositoryEventDispatcher repositoryEventDispatcher,
107                     RepositoryConnectorProvider repositoryConnectorProvider,
108                     RemoteRepositoryManager remoteRepositoryManager, UpdateCheckManager updateCheckManager,
109                     Set<MetadataGeneratorFactory> metadataFactories, SyncContextFactory syncContextFactory,
110                     OfflineController offlineController )
111    {
112        setFileProcessor( fileProcessor );
113        setRepositoryEventDispatcher( repositoryEventDispatcher );
114        setRepositoryConnectorProvider( repositoryConnectorProvider );
115        setRemoteRepositoryManager( remoteRepositoryManager );
116        setUpdateCheckManager( updateCheckManager );
117        setMetadataGeneratorFactories( metadataFactories );
118        setSyncContextFactory( syncContextFactory );
119        setOfflineController( offlineController );
120    }
121
122    public void initService( ServiceLocator locator )
123    {
124        setFileProcessor( locator.getService( FileProcessor.class ) );
125        setRepositoryEventDispatcher( locator.getService( RepositoryEventDispatcher.class ) );
126        setRepositoryConnectorProvider( locator.getService( RepositoryConnectorProvider.class ) );
127        setRemoteRepositoryManager( locator.getService( RemoteRepositoryManager.class ) );
128        setUpdateCheckManager( locator.getService( UpdateCheckManager.class ) );
129        setMetadataGeneratorFactories( locator.getServices( MetadataGeneratorFactory.class ) );
130        setSyncContextFactory( locator.getService( SyncContextFactory.class ) );
131        setOfflineController( locator.getService( OfflineController.class ) );
132    }
133
134    public DefaultDeployer setFileProcessor( FileProcessor fileProcessor )
135    {
136        this.fileProcessor = requireNonNull( fileProcessor, "file processor cannot be null" );
137        return this;
138    }
139
140    public DefaultDeployer setRepositoryEventDispatcher( RepositoryEventDispatcher repositoryEventDispatcher )
141    {
142        this.repositoryEventDispatcher = requireNonNull(
143                repositoryEventDispatcher, "repository event dispatcher cannot be null" );
144        return this;
145    }
146
147    public DefaultDeployer setRepositoryConnectorProvider( RepositoryConnectorProvider repositoryConnectorProvider )
148    {
149        this.repositoryConnectorProvider = requireNonNull(
150                repositoryConnectorProvider, "repository connector provider cannot be null" );
151        return this;
152    }
153
154    public DefaultDeployer setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager )
155    {
156        this.remoteRepositoryManager = requireNonNull(
157                remoteRepositoryManager, "remote repository provider cannot be null" );
158        return this;
159    }
160
161    public DefaultDeployer setUpdateCheckManager( UpdateCheckManager updateCheckManager )
162    {
163        this.updateCheckManager = requireNonNull( updateCheckManager, "update check manager cannot be null" );
164        return this;
165    }
166
167    public DefaultDeployer addMetadataGeneratorFactory( MetadataGeneratorFactory factory )
168    {
169        metadataFactories.add( requireNonNull( factory, "metadata generator factory cannot be null" ) );
170        return this;
171    }
172
173    public DefaultDeployer setMetadataGeneratorFactories( Collection<MetadataGeneratorFactory> metadataFactories )
174    {
175        if ( metadataFactories == null )
176        {
177            this.metadataFactories = new ArrayList<>();
178        }
179        else
180        {
181            this.metadataFactories = metadataFactories;
182        }
183        return this;
184    }
185
186    public DefaultDeployer setSyncContextFactory( SyncContextFactory syncContextFactory )
187    {
188        this.syncContextFactory = requireNonNull( syncContextFactory, "sync context factory cannot be null" );
189        return this;
190    }
191
192    public DefaultDeployer setOfflineController( OfflineController offlineController )
193    {
194        this.offlineController = requireNonNull( offlineController, "offline controller cannot be null" );
195        return this;
196    }
197
198    public DeployResult deploy( RepositorySystemSession session, DeployRequest request )
199        throws DeploymentException
200    {
201        try
202        {
203            Utils.checkOffline( session, offlineController, request.getRepository() );
204        }
205        catch ( RepositoryOfflineException e )
206        {
207            throw new DeploymentException( "Cannot deploy while " + request.getRepository().getId() + " ("
208                + request.getRepository().getUrl() + ") is in offline mode", e );
209        }
210
211        try ( SyncContext syncContext = syncContextFactory.newInstance( session, false ) )
212        {
213            return deploy( syncContext, session, request );
214        }
215    }
216
217    private DeployResult deploy( SyncContext syncContext, RepositorySystemSession session, DeployRequest request )
218        throws DeploymentException
219    {
220        DeployResult result = new DeployResult( request );
221
222        RequestTrace trace = RequestTrace.newChild( request.getTrace(), request );
223
224        RemoteRepository repository = request.getRepository();
225
226        RepositoryConnector connector;
227        try
228        {
229            connector = repositoryConnectorProvider.newRepositoryConnector( session, repository );
230        }
231        catch ( NoRepositoryConnectorException e )
232        {
233            throw new DeploymentException( "Failed to deploy artifacts/metadata: " + e.getMessage(), e );
234        }
235
236        try
237        {
238            List<? extends MetadataGenerator> generators = getMetadataGenerators( session, request );
239
240            FileTransformerManager fileTransformerManager = session.getFileTransformerManager();
241
242            List<ArtifactUpload> artifactUploads = new ArrayList<>();
243            List<MetadataUpload> metadataUploads = new ArrayList<>();
244            IdentityHashMap<Metadata, Object> processedMetadata = new IdentityHashMap<>();
245
246            EventCatapult catapult = new EventCatapult( session, trace, repository, repositoryEventDispatcher );
247
248            List<Artifact> artifacts = new ArrayList<>( request.getArtifacts() );
249
250            List<Metadata> metadatas = Utils.prepareMetadata( generators, artifacts );
251
252            syncContext.acquire( artifacts, Utils.combine( request.getMetadata(), metadatas ) );
253
254            for ( Metadata metadata : metadatas )
255            {
256                upload( metadataUploads, session, metadata, repository, connector, catapult );
257                processedMetadata.put( metadata, null );
258            }
259
260            for ( int i = 0; i < artifacts.size(); i++ )
261            {
262                Artifact artifact = artifacts.get( i );
263
264                for ( MetadataGenerator generator : generators )
265                {
266                    artifact = generator.transformArtifact( artifact );
267                }
268
269                artifacts.set( i, artifact );
270
271                Collection<FileTransformer> fileTransformers =
272                        fileTransformerManager.getTransformersForArtifact( artifact );
273                if ( !fileTransformers.isEmpty() )
274                {
275                    for ( FileTransformer fileTransformer : fileTransformers )
276                    {
277                        Artifact targetArtifact = fileTransformer.transformArtifact( artifact );
278
279                        ArtifactUpload upload = new ArtifactUpload( targetArtifact, artifact.getFile(),
280                                fileTransformer );
281                        upload.setTrace( trace );
282                        upload.setListener( new ArtifactUploadListener( catapult, upload ) );
283                        artifactUploads.add( upload );
284                    }
285                }
286                else
287                {
288                    ArtifactUpload upload = new ArtifactUpload( artifact, artifact.getFile() );
289                    upload.setTrace( trace );
290                    upload.setListener( new ArtifactUploadListener( catapult, upload ) );
291                    artifactUploads.add( upload );
292                }
293            }
294
295            connector.put( artifactUploads, null );
296
297            for ( ArtifactUpload upload : artifactUploads )
298            {
299                if ( upload.getException() != null )
300                {
301                    throw new DeploymentException( "Failed to deploy artifacts: " + upload.getException().getMessage(),
302                                                   upload.getException() );
303                }
304                result.addArtifact( upload.getArtifact() );
305            }
306
307            metadatas = Utils.finishMetadata( generators, artifacts );
308
309            syncContext.acquire( null, metadatas );
310
311            for ( Metadata metadata : metadatas )
312            {
313                upload( metadataUploads, session, metadata, repository, connector, catapult );
314                processedMetadata.put( metadata, null );
315            }
316
317            for ( Metadata metadata : request.getMetadata() )
318            {
319                if ( !processedMetadata.containsKey( metadata ) )
320                {
321                    upload( metadataUploads, session, metadata, repository, connector, catapult );
322                    processedMetadata.put( metadata, null );
323                }
324            }
325
326            connector.put( null, metadataUploads );
327
328            for ( MetadataUpload upload : metadataUploads )
329            {
330                if ( upload.getException() != null )
331                {
332                    throw new DeploymentException( "Failed to deploy metadata: " + upload.getException().getMessage(),
333                                                   upload.getException() );
334                }
335                result.addMetadata( upload.getMetadata() );
336            }
337        }
338        finally
339        {
340            connector.close();
341        }
342
343        return result;
344    }
345
346    private List<? extends MetadataGenerator> getMetadataGenerators( RepositorySystemSession session,
347                                                                     DeployRequest request )
348    {
349        PrioritizedComponents<MetadataGeneratorFactory> factories =
350            Utils.sortMetadataGeneratorFactories( session, this.metadataFactories );
351
352        List<MetadataGenerator> generators = new ArrayList<>();
353
354        for ( PrioritizedComponent<MetadataGeneratorFactory> factory : factories.getEnabled() )
355        {
356            MetadataGenerator generator = factory.getComponent().newInstance( session, request );
357            if ( generator != null )
358            {
359                generators.add( generator );
360            }
361        }
362
363        return generators;
364    }
365
366    private void upload( Collection<MetadataUpload> metadataUploads, RepositorySystemSession session,
367                         Metadata metadata, RemoteRepository repository, RepositoryConnector connector,
368                         EventCatapult catapult )
369        throws DeploymentException
370    {
371        LocalRepositoryManager lrm = session.getLocalRepositoryManager();
372        File basedir = lrm.getRepository().getBasedir();
373
374        File dstFile = new File( basedir, lrm.getPathForRemoteMetadata( metadata, repository, "" ) );
375
376        if ( metadata instanceof MergeableMetadata )
377        {
378            if ( !( (MergeableMetadata) metadata ).isMerged() )
379            {
380                RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.METADATA_RESOLVING );
381                event.setTrace( catapult.getTrace() );
382                event.setMetadata( metadata );
383                event.setRepository( repository );
384                repositoryEventDispatcher.dispatch( event.build() );
385
386                event = new RepositoryEvent.Builder( session, EventType.METADATA_DOWNLOADING );
387                event.setTrace( catapult.getTrace() );
388                event.setMetadata( metadata );
389                event.setRepository( repository );
390                repositoryEventDispatcher.dispatch( event.build() );
391
392                RepositoryPolicy policy = getPolicy( session, repository, metadata.getNature() );
393                MetadataDownload download = new MetadataDownload();
394                download.setMetadata( metadata );
395                download.setFile( dstFile );
396                download.setChecksumPolicy( policy.getChecksumPolicy() );
397                download.setListener( SafeTransferListener.wrap( session ) );
398                download.setTrace( catapult.getTrace() );
399                connector.get( null, Arrays.asList( download ) );
400
401                Exception error = download.getException();
402
403                if ( error instanceof MetadataNotFoundException )
404                {
405                    dstFile.delete();
406                }
407
408                event = new RepositoryEvent.Builder( session, EventType.METADATA_DOWNLOADED );
409                event.setTrace( catapult.getTrace() );
410                event.setMetadata( metadata );
411                event.setRepository( repository );
412                event.setException( error );
413                event.setFile( dstFile );
414                repositoryEventDispatcher.dispatch( event.build() );
415
416                event = new RepositoryEvent.Builder( session, EventType.METADATA_RESOLVED );
417                event.setTrace( catapult.getTrace() );
418                event.setMetadata( metadata );
419                event.setRepository( repository );
420                event.setException( error );
421                event.setFile( dstFile );
422                repositoryEventDispatcher.dispatch( event.build() );
423
424                if ( error != null && !( error instanceof MetadataNotFoundException ) )
425                {
426                    throw new DeploymentException( "Failed to retrieve remote metadata " + metadata + ": "
427                        + error.getMessage(), error );
428                }
429            }
430
431            try
432            {
433                ( (MergeableMetadata) metadata ).merge( dstFile, dstFile );
434            }
435            catch ( RepositoryException e )
436            {
437                throw new DeploymentException( "Failed to update metadata " + metadata + ": " + e.getMessage(), e );
438            }
439        }
440        else
441        {
442            if ( metadata.getFile() == null )
443            {
444                throw new DeploymentException( "Failed to update metadata " + metadata + ": No file attached." );
445            }
446            try
447            {
448                fileProcessor.copy( metadata.getFile(), dstFile );
449            }
450            catch ( IOException e )
451            {
452                throw new DeploymentException( "Failed to update metadata " + metadata + ": " + e.getMessage(), e );
453            }
454        }
455
456        UpdateCheck<Metadata, MetadataTransferException> check = new UpdateCheck<>();
457        check.setItem( metadata );
458        check.setFile( dstFile );
459        check.setRepository( repository );
460        check.setAuthoritativeRepository( repository );
461        updateCheckManager.touchMetadata( session, check );
462
463        MetadataUpload upload = new MetadataUpload( metadata, dstFile );
464        upload.setTrace( catapult.getTrace() );
465        upload.setListener( new MetadataUploadListener( catapult, upload ) );
466        metadataUploads.add( upload );
467    }
468
469    private RepositoryPolicy getPolicy( RepositorySystemSession session, RemoteRepository repository,
470                                        Metadata.Nature nature )
471    {
472        boolean releases = !Metadata.Nature.SNAPSHOT.equals( nature );
473        boolean snapshots = !Metadata.Nature.RELEASE.equals( nature );
474        return remoteRepositoryManager.getPolicy( session, repository, releases, snapshots );
475    }
476
477    static final class EventCatapult
478    {
479
480        private final RepositorySystemSession session;
481
482        private final RequestTrace trace;
483
484        private final RemoteRepository repository;
485
486        private final RepositoryEventDispatcher dispatcher;
487
488        EventCatapult( RepositorySystemSession session, RequestTrace trace, RemoteRepository repository,
489                              RepositoryEventDispatcher dispatcher )
490        {
491            this.session = session;
492            this.trace = trace;
493            this.repository = repository;
494            this.dispatcher = dispatcher;
495        }
496
497        public RepositorySystemSession getSession()
498        {
499            return session;
500        }
501
502        public RequestTrace getTrace()
503        {
504            return trace;
505        }
506
507        public void artifactDeploying( Artifact artifact, File file )
508        {
509            RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_DEPLOYING );
510            event.setTrace( trace );
511            event.setArtifact( artifact );
512            event.setRepository( repository );
513            event.setFile( file );
514
515            dispatcher.dispatch( event.build() );
516        }
517
518        public void artifactDeployed( Artifact artifact, File file, ArtifactTransferException exception )
519        {
520            RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_DEPLOYED );
521            event.setTrace( trace );
522            event.setArtifact( artifact );
523            event.setRepository( repository );
524            event.setFile( file );
525            event.setException( exception );
526
527            dispatcher.dispatch( event.build() );
528        }
529
530        public void metadataDeploying( Metadata metadata, File file )
531        {
532            RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.METADATA_DEPLOYING );
533            event.setTrace( trace );
534            event.setMetadata( metadata );
535            event.setRepository( repository );
536            event.setFile( file );
537
538            dispatcher.dispatch( event.build() );
539        }
540
541        public void metadataDeployed( Metadata metadata, File file, Exception exception )
542        {
543            RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.METADATA_DEPLOYED );
544            event.setTrace( trace );
545            event.setMetadata( metadata );
546            event.setRepository( repository );
547            event.setFile( file );
548            event.setException( exception );
549
550            dispatcher.dispatch( event.build() );
551        }
552
553    }
554
555    static final class ArtifactUploadListener
556        extends SafeTransferListener
557    {
558
559        private final EventCatapult catapult;
560
561        private final ArtifactUpload transfer;
562
563        ArtifactUploadListener( EventCatapult catapult, ArtifactUpload transfer )
564        {
565            super( catapult.getSession() );
566            this.catapult = catapult;
567            this.transfer = transfer;
568        }
569
570        @Override
571        public void transferInitiated( TransferEvent event )
572            throws TransferCancelledException
573        {
574            super.transferInitiated( event );
575            catapult.artifactDeploying( transfer.getArtifact(), transfer.getFile() );
576        }
577
578        @Override
579        public void transferFailed( TransferEvent event )
580        {
581            super.transferFailed( event );
582            catapult.artifactDeployed( transfer.getArtifact(), transfer.getFile(), transfer.getException() );
583        }
584
585        @Override
586        public void transferSucceeded( TransferEvent event )
587        {
588            super.transferSucceeded( event );
589            catapult.artifactDeployed( transfer.getArtifact(), transfer.getFile(), null );
590        }
591
592    }
593
594    static final class MetadataUploadListener
595        extends SafeTransferListener
596    {
597
598        private final EventCatapult catapult;
599
600        private final MetadataUpload transfer;
601
602        MetadataUploadListener( EventCatapult catapult, MetadataUpload transfer )
603        {
604            super( catapult.getSession() );
605            this.catapult = catapult;
606            this.transfer = transfer;
607        }
608
609        @Override
610        public void transferInitiated( TransferEvent event )
611            throws TransferCancelledException
612        {
613            super.transferInitiated( event );
614            catapult.metadataDeploying( transfer.getMetadata(), transfer.getFile() );
615        }
616
617        @Override
618        public void transferFailed( TransferEvent event )
619        {
620            super.transferFailed( event );
621            catapult.metadataDeployed( transfer.getMetadata(), transfer.getFile(), transfer.getException() );
622        }
623
624        @Override
625        public void transferSucceeded( TransferEvent event )
626        {
627            super.transferSucceeded( event );
628            catapult.metadataDeployed( transfer.getMetadata(), transfer.getFile(), null );
629        }
630
631    }
632
633}