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        requireNonNull( session, "session cannot be null" );
206        requireNonNull( request, "request cannot be null" );
207        try
208        {
209            Utils.checkOffline( session, offlineController, request.getRepository() );
210        }
211        catch ( RepositoryOfflineException e )
212        {
213            throw new DeploymentException( "Cannot deploy while " + request.getRepository().getId() + " ("
214                + request.getRepository().getUrl() + ") is in offline mode", e );
215        }
216
217        try ( SyncContext syncContext = syncContextFactory.newInstance( session, false ) )
218        {
219            return deploy( syncContext, session, request );
220        }
221    }
222
223    private DeployResult deploy( SyncContext syncContext, RepositorySystemSession session, DeployRequest request )
224        throws DeploymentException
225    {
226        DeployResult result = new DeployResult( request );
227
228        RequestTrace trace = RequestTrace.newChild( request.getTrace(), request );
229
230        RemoteRepository repository = request.getRepository();
231
232        RepositoryConnector connector;
233        try
234        {
235            connector = repositoryConnectorProvider.newRepositoryConnector( session, repository );
236        }
237        catch ( NoRepositoryConnectorException e )
238        {
239            throw new DeploymentException( "Failed to deploy artifacts/metadata: " + e.getMessage(), e );
240        }
241
242        try
243        {
244            List<? extends MetadataGenerator> generators = getMetadataGenerators( session, request );
245
246            FileTransformerManager fileTransformerManager = session.getFileTransformerManager();
247
248            List<ArtifactUpload> artifactUploads = new ArrayList<>();
249            List<MetadataUpload> metadataUploads = new ArrayList<>();
250            IdentityHashMap<Metadata, Object> processedMetadata = new IdentityHashMap<>();
251
252            EventCatapult catapult = new EventCatapult( session, trace, repository, repositoryEventDispatcher );
253
254            List<Artifact> artifacts = new ArrayList<>( request.getArtifacts() );
255
256            List<Metadata> metadatas = Utils.prepareMetadata( generators, artifacts );
257
258            syncContext.acquire( artifacts, Utils.combine( request.getMetadata(), metadatas ) );
259
260            for ( Metadata metadata : metadatas )
261            {
262                upload( metadataUploads, session, metadata, repository, connector, catapult );
263                processedMetadata.put( metadata, null );
264            }
265
266            for ( ListIterator<Artifact> iterator = artifacts.listIterator(); iterator.hasNext(); )
267            {
268                Artifact artifact = iterator.next();
269
270                for ( MetadataGenerator generator : generators )
271                {
272                    artifact = generator.transformArtifact( artifact );
273                }
274
275                iterator.set( artifact );
276
277                Collection<FileTransformer> fileTransformers =
278                        fileTransformerManager.getTransformersForArtifact( artifact );
279                if ( !fileTransformers.isEmpty() )
280                {
281                    for ( FileTransformer fileTransformer : fileTransformers )
282                    {
283                        Artifact targetArtifact = fileTransformer.transformArtifact( artifact );
284
285                        ArtifactUpload upload = new ArtifactUpload( targetArtifact, artifact.getFile(),
286                                fileTransformer );
287                        upload.setTrace( trace );
288                        upload.setListener( new ArtifactUploadListener( catapult, upload ) );
289                        artifactUploads.add( upload );
290                    }
291                }
292                else
293                {
294                    ArtifactUpload upload = new ArtifactUpload( artifact, artifact.getFile() );
295                    upload.setTrace( trace );
296                    upload.setListener( new ArtifactUploadListener( catapult, upload ) );
297                    artifactUploads.add( upload );
298                }
299            }
300
301            connector.put( artifactUploads, null );
302
303            for ( ArtifactUpload upload : artifactUploads )
304            {
305                if ( upload.getException() != null )
306                {
307                    throw new DeploymentException( "Failed to deploy artifacts: " + upload.getException().getMessage(),
308                                                   upload.getException() );
309                }
310                result.addArtifact( upload.getArtifact() );
311            }
312
313            metadatas = Utils.finishMetadata( generators, artifacts );
314
315            syncContext.acquire( null, metadatas );
316
317            for ( Metadata metadata : metadatas )
318            {
319                upload( metadataUploads, session, metadata, repository, connector, catapult );
320                processedMetadata.put( metadata, null );
321            }
322
323            for ( Metadata metadata : request.getMetadata() )
324            {
325                if ( !processedMetadata.containsKey( metadata ) )
326                {
327                    upload( metadataUploads, session, metadata, repository, connector, catapult );
328                    processedMetadata.put( metadata, null );
329                }
330            }
331
332            connector.put( null, metadataUploads );
333
334            for ( MetadataUpload upload : metadataUploads )
335            {
336                if ( upload.getException() != null )
337                {
338                    throw new DeploymentException( "Failed to deploy metadata: " + upload.getException().getMessage(),
339                                                   upload.getException() );
340                }
341                result.addMetadata( upload.getMetadata() );
342            }
343        }
344        finally
345        {
346            connector.close();
347        }
348
349        return result;
350    }
351
352    private List<? extends MetadataGenerator> getMetadataGenerators( RepositorySystemSession session,
353                                                                     DeployRequest request )
354    {
355        PrioritizedComponents<MetadataGeneratorFactory> factories =
356            Utils.sortMetadataGeneratorFactories( session, this.metadataFactories );
357
358        List<MetadataGenerator> generators = new ArrayList<>();
359
360        for ( PrioritizedComponent<MetadataGeneratorFactory> factory : factories.getEnabled() )
361        {
362            MetadataGenerator generator = factory.getComponent().newInstance( session, request );
363            if ( generator != null )
364            {
365                generators.add( generator );
366            }
367        }
368
369        return generators;
370    }
371
372    private void upload( Collection<MetadataUpload> metadataUploads, RepositorySystemSession session,
373                         Metadata metadata, RemoteRepository repository, RepositoryConnector connector,
374                         EventCatapult catapult )
375        throws DeploymentException
376    {
377        LocalRepositoryManager lrm = session.getLocalRepositoryManager();
378        File basedir = lrm.getRepository().getBasedir();
379
380        File dstFile = new File( basedir, lrm.getPathForRemoteMetadata( metadata, repository, "" ) );
381
382        if ( metadata instanceof MergeableMetadata )
383        {
384            if ( !( (MergeableMetadata) metadata ).isMerged() )
385            {
386                RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.METADATA_RESOLVING );
387                event.setTrace( catapult.getTrace() );
388                event.setMetadata( metadata );
389                event.setRepository( repository );
390                repositoryEventDispatcher.dispatch( event.build() );
391
392                event = new RepositoryEvent.Builder( session, EventType.METADATA_DOWNLOADING );
393                event.setTrace( catapult.getTrace() );
394                event.setMetadata( metadata );
395                event.setRepository( repository );
396                repositoryEventDispatcher.dispatch( event.build() );
397
398                RepositoryPolicy policy = getPolicy( session, repository, metadata.getNature() );
399                MetadataDownload download = new MetadataDownload();
400                download.setMetadata( metadata );
401                download.setFile( dstFile );
402                download.setChecksumPolicy( policy.getChecksumPolicy() );
403                download.setListener( SafeTransferListener.wrap( session ) );
404                download.setTrace( catapult.getTrace() );
405                connector.get( null, Arrays.asList( download ) );
406
407                Exception error = download.getException();
408
409                if ( error instanceof MetadataNotFoundException )
410                {
411                    dstFile.delete();
412                }
413
414                event = new RepositoryEvent.Builder( session, EventType.METADATA_DOWNLOADED );
415                event.setTrace( catapult.getTrace() );
416                event.setMetadata( metadata );
417                event.setRepository( repository );
418                event.setException( error );
419                event.setFile( dstFile );
420                repositoryEventDispatcher.dispatch( event.build() );
421
422                event = new RepositoryEvent.Builder( session, EventType.METADATA_RESOLVED );
423                event.setTrace( catapult.getTrace() );
424                event.setMetadata( metadata );
425                event.setRepository( repository );
426                event.setException( error );
427                event.setFile( dstFile );
428                repositoryEventDispatcher.dispatch( event.build() );
429
430                if ( error != null && !( error instanceof MetadataNotFoundException ) )
431                {
432                    throw new DeploymentException( "Failed to retrieve remote metadata " + metadata + ": "
433                        + error.getMessage(), error );
434                }
435            }
436
437            try
438            {
439                ( (MergeableMetadata) metadata ).merge( dstFile, dstFile );
440            }
441            catch ( RepositoryException e )
442            {
443                throw new DeploymentException( "Failed to update metadata " + metadata + ": " + e.getMessage(), e );
444            }
445        }
446        else
447        {
448            if ( metadata.getFile() == null )
449            {
450                throw new DeploymentException( "Failed to update metadata " + metadata + ": No file attached." );
451            }
452            try
453            {
454                fileProcessor.copy( metadata.getFile(), dstFile );
455            }
456            catch ( IOException e )
457            {
458                throw new DeploymentException( "Failed to update metadata " + metadata + ": " + e.getMessage(), e );
459            }
460        }
461
462        UpdateCheck<Metadata, MetadataTransferException> check = new UpdateCheck<>();
463        check.setItem( metadata );
464        check.setFile( dstFile );
465        check.setRepository( repository );
466        check.setAuthoritativeRepository( repository );
467        updateCheckManager.touchMetadata( session, check );
468
469        MetadataUpload upload = new MetadataUpload( metadata, dstFile );
470        upload.setTrace( catapult.getTrace() );
471        upload.setListener( new MetadataUploadListener( catapult, upload ) );
472        metadataUploads.add( upload );
473    }
474
475    private RepositoryPolicy getPolicy( RepositorySystemSession session, RemoteRepository repository,
476                                        Metadata.Nature nature )
477    {
478        boolean releases = !Metadata.Nature.SNAPSHOT.equals( nature );
479        boolean snapshots = !Metadata.Nature.RELEASE.equals( nature );
480        return remoteRepositoryManager.getPolicy( session, repository, releases, snapshots );
481    }
482
483    static final class EventCatapult
484    {
485
486        private final RepositorySystemSession session;
487
488        private final RequestTrace trace;
489
490        private final RemoteRepository repository;
491
492        private final RepositoryEventDispatcher dispatcher;
493
494        EventCatapult( RepositorySystemSession session, RequestTrace trace, RemoteRepository repository,
495                              RepositoryEventDispatcher dispatcher )
496        {
497            this.session = session;
498            this.trace = trace;
499            this.repository = repository;
500            this.dispatcher = dispatcher;
501        }
502
503        public RepositorySystemSession getSession()
504        {
505            return session;
506        }
507
508        public RequestTrace getTrace()
509        {
510            return trace;
511        }
512
513        public void artifactDeploying( Artifact artifact, File file )
514        {
515            RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_DEPLOYING );
516            event.setTrace( trace );
517            event.setArtifact( artifact );
518            event.setRepository( repository );
519            event.setFile( file );
520
521            dispatcher.dispatch( event.build() );
522        }
523
524        public void artifactDeployed( Artifact artifact, File file, ArtifactTransferException exception )
525        {
526            RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_DEPLOYED );
527            event.setTrace( trace );
528            event.setArtifact( artifact );
529            event.setRepository( repository );
530            event.setFile( file );
531            event.setException( exception );
532
533            dispatcher.dispatch( event.build() );
534        }
535
536        public void metadataDeploying( Metadata metadata, File file )
537        {
538            RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.METADATA_DEPLOYING );
539            event.setTrace( trace );
540            event.setMetadata( metadata );
541            event.setRepository( repository );
542            event.setFile( file );
543
544            dispatcher.dispatch( event.build() );
545        }
546
547        public void metadataDeployed( Metadata metadata, File file, Exception exception )
548        {
549            RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.METADATA_DEPLOYED );
550            event.setTrace( trace );
551            event.setMetadata( metadata );
552            event.setRepository( repository );
553            event.setFile( file );
554            event.setException( exception );
555
556            dispatcher.dispatch( event.build() );
557        }
558
559    }
560
561    static final class ArtifactUploadListener
562        extends SafeTransferListener
563    {
564
565        private final EventCatapult catapult;
566
567        private final ArtifactUpload transfer;
568
569        ArtifactUploadListener( EventCatapult catapult, ArtifactUpload transfer )
570        {
571            super( catapult.getSession() );
572            this.catapult = catapult;
573            this.transfer = transfer;
574        }
575
576        @Override
577        public void transferInitiated( TransferEvent event )
578            throws TransferCancelledException
579        {
580            super.transferInitiated( event );
581            requireNonNull( event, "event cannot be null" );
582            catapult.artifactDeploying( transfer.getArtifact(), transfer.getFile() );
583        }
584
585        @Override
586        public void transferFailed( TransferEvent event )
587        {
588            super.transferFailed( event );
589            requireNonNull( event, "event cannot be null" );
590            catapult.artifactDeployed( transfer.getArtifact(), transfer.getFile(), transfer.getException() );
591        }
592
593        @Override
594        public void transferSucceeded( TransferEvent event )
595        {
596            super.transferSucceeded( event );
597            requireNonNull( event, "event cannot be null" );
598            catapult.artifactDeployed( transfer.getArtifact(), transfer.getFile(), null );
599        }
600
601    }
602
603    static final class MetadataUploadListener
604        extends SafeTransferListener
605    {
606
607        private final EventCatapult catapult;
608
609        private final MetadataUpload transfer;
610
611        MetadataUploadListener( EventCatapult catapult, MetadataUpload transfer )
612        {
613            super( catapult.getSession() );
614            this.catapult = catapult;
615            this.transfer = transfer;
616        }
617
618        @Override
619        public void transferInitiated( TransferEvent event )
620            throws TransferCancelledException
621        {
622            super.transferInitiated( event );
623            requireNonNull( event, "event cannot be null" );
624            catapult.metadataDeploying( transfer.getMetadata(), transfer.getFile() );
625        }
626
627        @Override
628        public void transferFailed( TransferEvent event )
629        {
630            super.transferFailed( event );
631            requireNonNull( event, "event cannot be null" );
632            catapult.metadataDeployed( transfer.getMetadata(), transfer.getFile(), transfer.getException() );
633        }
634
635        @Override
636        public void transferSucceeded( TransferEvent event )
637        {
638            super.transferSucceeded( event );
639            requireNonNull( event, "event cannot be null" );
640            catapult.metadataDeployed( transfer.getMetadata(), transfer.getFile(), null );
641        }
642
643    }
644
645}