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