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