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