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