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 static java.util.Objects.requireNonNull;
023
024import java.io.File;
025import java.io.InputStream;
026import java.util.ArrayList;
027import java.util.Collection;
028import java.util.IdentityHashMap;
029import java.util.List;
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.RepositorySystemSession;
038import org.eclipse.aether.RequestTrace;
039import org.eclipse.aether.SyncContext;
040import org.eclipse.aether.artifact.Artifact;
041import org.eclipse.aether.impl.Installer;
042import org.eclipse.aether.impl.MetadataGenerator;
043import org.eclipse.aether.impl.MetadataGeneratorFactory;
044import org.eclipse.aether.impl.RepositoryEventDispatcher;
045import org.eclipse.aether.impl.SyncContextFactory;
046import org.eclipse.aether.installation.InstallRequest;
047import org.eclipse.aether.installation.InstallResult;
048import org.eclipse.aether.installation.InstallationException;
049import org.eclipse.aether.metadata.MergeableMetadata;
050import org.eclipse.aether.metadata.Metadata;
051import org.eclipse.aether.repository.LocalArtifactRegistration;
052import org.eclipse.aether.repository.LocalMetadataRegistration;
053import org.eclipse.aether.repository.LocalRepositoryManager;
054import org.eclipse.aether.spi.io.FileProcessor;
055import org.eclipse.aether.spi.locator.Service;
056import org.eclipse.aether.spi.locator.ServiceLocator;
057import org.eclipse.aether.transform.FileTransformer;
058import org.slf4j.Logger;
059import org.slf4j.LoggerFactory;
060
061/**
062 */
063@Named
064public class DefaultInstaller
065    implements Installer, Service
066{
067
068    private static final Logger LOGGER = LoggerFactory.getLogger( DefaultInstaller.class );
069
070    private FileProcessor fileProcessor;
071
072    private RepositoryEventDispatcher repositoryEventDispatcher;
073
074    private Collection<MetadataGeneratorFactory> metadataFactories = new ArrayList<MetadataGeneratorFactory>();
075
076    private SyncContextFactory syncContextFactory;
077
078    public DefaultInstaller()
079    {
080        // enables default constructor
081    }
082
083    @Inject
084    DefaultInstaller( FileProcessor fileProcessor, RepositoryEventDispatcher repositoryEventDispatcher,
085                      Set<MetadataGeneratorFactory> metadataFactories, SyncContextFactory syncContextFactory )
086    {
087        setFileProcessor( fileProcessor );
088        setRepositoryEventDispatcher( repositoryEventDispatcher );
089        setMetadataGeneratorFactories( metadataFactories );
090        setSyncContextFactory( syncContextFactory );
091    }
092
093    public void initService( ServiceLocator locator )
094    {
095        setFileProcessor( locator.getService( FileProcessor.class ) );
096        setRepositoryEventDispatcher( locator.getService( RepositoryEventDispatcher.class ) );
097        setMetadataGeneratorFactories( locator.getServices( MetadataGeneratorFactory.class ) );
098        setSyncContextFactory( locator.getService( SyncContextFactory.class ) );
099    }
100
101    public DefaultInstaller setFileProcessor( FileProcessor fileProcessor )
102    {
103        this.fileProcessor = requireNonNull( fileProcessor, "file processor cannot be null" );
104        return this;
105    }
106
107    public DefaultInstaller setRepositoryEventDispatcher( RepositoryEventDispatcher repositoryEventDispatcher )
108    {
109        this.repositoryEventDispatcher = requireNonNull( repositoryEventDispatcher, "repository event dispatcher cannot be null" );
110        return this;
111    }
112
113    public DefaultInstaller addMetadataGeneratorFactory( MetadataGeneratorFactory factory )
114    {
115        metadataFactories.add( requireNonNull( factory, "metadata generator factory cannot be null" ) );
116        return this;
117    }
118
119    public DefaultInstaller setMetadataGeneratorFactories( Collection<MetadataGeneratorFactory> metadataFactories )
120    {
121        if ( metadataFactories == null )
122        {
123            this.metadataFactories = new ArrayList<MetadataGeneratorFactory>();
124        }
125        else
126        {
127            this.metadataFactories = metadataFactories;
128        }
129        return this;
130    }
131
132    public DefaultInstaller setSyncContextFactory( SyncContextFactory syncContextFactory )
133    {
134        this.syncContextFactory = requireNonNull( syncContextFactory, "sync context factory cannot be null" );
135        return this;
136    }
137
138    public InstallResult install( RepositorySystemSession session, InstallRequest request )
139        throws InstallationException
140    {
141        SyncContext syncContext = syncContextFactory.newInstance( session, false );
142
143        try
144        {
145            return install( syncContext, session, request );
146        }
147        finally
148        {
149            syncContext.close();
150        }
151    }
152
153    private InstallResult install( SyncContext syncContext, RepositorySystemSession session, InstallRequest request )
154        throws InstallationException
155    {
156        InstallResult result = new InstallResult( request );
157
158        RequestTrace trace = RequestTrace.newChild( request.getTrace(), request );
159
160        List<? extends MetadataGenerator> generators = getMetadataGenerators( session, request );
161
162        List<Artifact> artifacts = new ArrayList<Artifact>( request.getArtifacts() );
163
164        IdentityHashMap<Metadata, Object> processedMetadata = new IdentityHashMap<Metadata, Object>();
165
166        List<Metadata> metadatas = Utils.prepareMetadata( generators, artifacts );
167
168        syncContext.acquire( artifacts, Utils.combine( request.getMetadata(), metadatas ) );
169
170        for ( Metadata metadata : metadatas )
171        {
172            install( session, trace, metadata );
173            processedMetadata.put( metadata, null );
174            result.addMetadata( metadata );
175        }
176
177        for ( int i = 0; i < artifacts.size(); i++ )
178        {
179            Artifact artifact = artifacts.get( i );
180
181            for ( MetadataGenerator generator : generators )
182            {
183                artifact = generator.transformArtifact( artifact );
184            }
185
186            artifacts.set( i, artifact );
187
188            install( session, trace, artifact );
189            result.addArtifact( artifact );
190        }
191
192        metadatas = Utils.finishMetadata( generators, artifacts );
193
194        syncContext.acquire( null, metadatas );
195
196        for ( Metadata metadata : metadatas )
197        {
198            install( session, trace, metadata );
199            processedMetadata.put( metadata, null );
200            result.addMetadata( metadata );
201        }
202
203        for ( Metadata metadata : request.getMetadata() )
204        {
205            if ( !processedMetadata.containsKey( metadata ) )
206            {
207                install( session, trace, metadata );
208                result.addMetadata( metadata );
209            }
210        }
211
212        return result;
213    }
214
215    private List<? extends MetadataGenerator> getMetadataGenerators( RepositorySystemSession session,
216                                                                     InstallRequest request )
217    {
218        PrioritizedComponents<MetadataGeneratorFactory> factories =
219            Utils.sortMetadataGeneratorFactories( session, this.metadataFactories );
220
221        List<MetadataGenerator> generators = new ArrayList<MetadataGenerator>();
222
223        for ( PrioritizedComponent<MetadataGeneratorFactory> factory : factories.getEnabled() )
224        {
225            MetadataGenerator generator = factory.getComponent().newInstance( session, request );
226            if ( generator != null )
227            {
228                generators.add( generator );
229            }
230        }
231
232        return generators;
233    }
234
235    private void install( RepositorySystemSession session, RequestTrace trace, Artifact artifact )
236        throws InstallationException
237    {
238        LocalRepositoryManager lrm = session.getLocalRepositoryManager();
239
240        File srcFile = artifact.getFile();
241
242        Collection<FileTransformer> fileTransformers = session.getFileTransformerManager().getTransformersForArtifact( artifact );
243        if ( fileTransformers.isEmpty() )
244        {
245            install( session, trace, artifact, lrm, srcFile, null );
246        }
247        else
248        {
249            for ( FileTransformer fileTransformer : fileTransformers )
250            {
251                install( session, trace, artifact, lrm, srcFile, fileTransformer );
252            }
253        }
254    }
255
256    private void install( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
257                          LocalRepositoryManager lrm, File srcFile, FileTransformer fileTransformer )
258        throws InstallationException
259    {
260        final Artifact targetArtifact;
261        if ( fileTransformer != null )
262        {
263            targetArtifact = fileTransformer.transformArtifact( artifact );
264        }
265        else
266        {
267            targetArtifact = artifact;
268        }
269
270        File dstFile = new File( lrm.getRepository().getBasedir(), lrm.getPathForLocalArtifact( targetArtifact ) );
271
272        artifactInstalling( session, trace, targetArtifact, dstFile );
273
274        Exception exception = null;
275        try
276        {
277            if ( dstFile.equals( srcFile ) )
278            {
279                throw new IllegalStateException( "cannot install " + dstFile + " to same path" );
280            }
281
282            boolean copy =
283                "pom".equals( targetArtifact.getExtension() ) || srcFile.lastModified() != dstFile.lastModified()
284                    || srcFile.length() != dstFile.length() || !srcFile.exists();
285
286            if ( !copy )
287            {
288                LOGGER.debug( "Skipped re-installing {} to {}, seems unchanged", srcFile, dstFile );
289            }
290            else if ( fileTransformer != null ) 
291            {
292                try ( InputStream is = fileTransformer.transformData( srcFile ) )
293                {
294                    fileProcessor.write( dstFile, is );
295                    dstFile.setLastModified( srcFile.lastModified() );
296                }
297            }
298            else
299            {
300                fileProcessor.copy( srcFile, dstFile );
301                dstFile.setLastModified( srcFile.lastModified() );
302            }
303
304            lrm.add( session, new LocalArtifactRegistration( targetArtifact ) );
305        }
306        catch ( Exception e )
307        {
308            exception = e;
309            throw new InstallationException( "Failed to install artifact " + targetArtifact + ": " + e.getMessage(), e );
310        }
311        finally
312        {
313            artifactInstalled( session, trace, targetArtifact, dstFile, exception );
314        }
315    }
316
317    private void install( RepositorySystemSession session, RequestTrace trace, Metadata metadata )
318        throws InstallationException
319    {
320        LocalRepositoryManager lrm = session.getLocalRepositoryManager();
321
322        File dstFile = new File( lrm.getRepository().getBasedir(), lrm.getPathForLocalMetadata( metadata ) );
323
324        metadataInstalling( session, trace, metadata, dstFile );
325
326        Exception exception = null;
327        try
328        {
329            if ( metadata instanceof MergeableMetadata )
330            {
331                ( (MergeableMetadata) metadata ).merge( dstFile, dstFile );
332            }
333            else
334            {
335                if ( dstFile.equals( metadata.getFile() ) )
336                {
337                    throw new IllegalStateException( "cannot install " + dstFile + " to same path" );
338                }
339                fileProcessor.copy( metadata.getFile(), dstFile );
340            }
341
342            lrm.add( session, new LocalMetadataRegistration( metadata ) );
343        }
344        catch ( Exception e )
345        {
346            exception = e;
347            throw new InstallationException( "Failed to install metadata " + metadata + ": " + e.getMessage(), e );
348        }
349        finally
350        {
351            metadataInstalled( session, trace, metadata, dstFile, exception );
352        }
353    }
354
355    private void artifactInstalling( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
356                                     File dstFile )
357    {
358        RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_INSTALLING );
359        event.setTrace( trace );
360        event.setArtifact( artifact );
361        event.setRepository( session.getLocalRepositoryManager().getRepository() );
362        event.setFile( dstFile );
363
364        repositoryEventDispatcher.dispatch( event.build() );
365    }
366
367    private void artifactInstalled( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
368                                    File dstFile, Exception exception )
369    {
370        RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_INSTALLED );
371        event.setTrace( trace );
372        event.setArtifact( artifact );
373        event.setRepository( session.getLocalRepositoryManager().getRepository() );
374        event.setFile( dstFile );
375        event.setException( exception );
376
377        repositoryEventDispatcher.dispatch( event.build() );
378    }
379
380    private void metadataInstalling( RepositorySystemSession session, RequestTrace trace, Metadata metadata,
381                                     File dstFile )
382    {
383        RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.METADATA_INSTALLING );
384        event.setTrace( trace );
385        event.setMetadata( metadata );
386        event.setRepository( session.getLocalRepositoryManager().getRepository() );
387        event.setFile( dstFile );
388
389        repositoryEventDispatcher.dispatch( event.build() );
390    }
391
392    private void metadataInstalled( RepositorySystemSession session, RequestTrace trace, Metadata metadata,
393                                    File dstFile, Exception exception )
394    {
395        RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.METADATA_INSTALLED );
396        event.setTrace( trace );
397        event.setMetadata( metadata );
398        event.setRepository( session.getLocalRepositoryManager().getRepository() );
399        event.setFile( dstFile );
400        event.setException( exception );
401
402        repositoryEventDispatcher.dispatch( event.build() );
403    }
404
405}