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