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        requireNonNull( session, "session cannot be null" );
146        requireNonNull( request, "request cannot be null" );
147        try ( SyncContext syncContext = syncContextFactory.newInstance( session, false ) )
148        {
149            return install( syncContext, session, request );
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<>( request.getArtifacts() );
163
164        IdentityHashMap<Metadata, Object> processedMetadata = new IdentityHashMap<>();
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 ( ListIterator<Artifact> iterator = artifacts.listIterator(); iterator.hasNext(); )
178        {
179            Artifact artifact = iterator.next();
180
181            for ( MetadataGenerator generator : generators )
182            {
183                artifact = generator.transformArtifact( artifact );
184            }
185
186            iterator.set( 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<>();
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()
243                .getTransformersForArtifact( artifact );
244        if ( fileTransformers.isEmpty() )
245        {
246            install( session, trace, artifact, lrm, srcFile, null );
247        }
248        else
249        {
250            for ( FileTransformer fileTransformer : fileTransformers )
251            {
252                install( session, trace, artifact, lrm, srcFile, fileTransformer );
253            }
254        }
255    }
256
257    private void install( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
258                          LocalRepositoryManager lrm, File srcFile, FileTransformer fileTransformer )
259        throws InstallationException
260    {
261        final Artifact targetArtifact;
262        if ( fileTransformer != null )
263        {
264            targetArtifact = fileTransformer.transformArtifact( artifact );
265        }
266        else
267        {
268            targetArtifact = artifact;
269        }
270
271        File dstFile = new File( lrm.getRepository().getBasedir(), lrm.getPathForLocalArtifact( targetArtifact ) );
272
273        artifactInstalling( session, trace, targetArtifact, dstFile );
274
275        Exception exception = null;
276        try
277        {
278            if ( dstFile.equals( srcFile ) )
279            {
280                throw new IllegalStateException( "cannot install " + dstFile + " to same path" );
281            }
282
283            boolean copy =
284                "pom".equals( targetArtifact.getExtension() ) || srcFile.lastModified() != dstFile.lastModified()
285                    || srcFile.length() != dstFile.length() || !srcFile.exists();
286
287            if ( !copy )
288            {
289                LOGGER.debug( "Skipped re-installing {} to {}, seems unchanged", srcFile, dstFile );
290            }
291            else if ( fileTransformer != null ) 
292            {
293                try ( InputStream is = fileTransformer.transformData( srcFile ) )
294                {
295                    fileProcessor.write( dstFile, is );
296                    dstFile.setLastModified( srcFile.lastModified() );
297                }
298            }
299            else
300            {
301                fileProcessor.copy( srcFile, dstFile );
302                dstFile.setLastModified( srcFile.lastModified() );
303            }
304
305            lrm.add( session, new LocalArtifactRegistration( targetArtifact ) );
306        }
307        catch ( Exception e )
308        {
309            exception = e;
310            throw new InstallationException( "Failed to install artifact " + targetArtifact + ": " + e.getMessage(),
311                    e );
312        }
313        finally
314        {
315            artifactInstalled( session, trace, targetArtifact, dstFile, exception );
316        }
317    }
318
319    private void install( RepositorySystemSession session, RequestTrace trace, Metadata metadata )
320        throws InstallationException
321    {
322        LocalRepositoryManager lrm = session.getLocalRepositoryManager();
323
324        File dstFile = new File( lrm.getRepository().getBasedir(), lrm.getPathForLocalMetadata( metadata ) );
325
326        metadataInstalling( session, trace, metadata, dstFile );
327
328        Exception exception = null;
329        try
330        {
331            if ( metadata instanceof MergeableMetadata )
332            {
333                ( (MergeableMetadata) metadata ).merge( dstFile, dstFile );
334            }
335            else
336            {
337                if ( dstFile.equals( metadata.getFile() ) )
338                {
339                    throw new IllegalStateException( "cannot install " + dstFile + " to same path" );
340                }
341                fileProcessor.copy( metadata.getFile(), dstFile );
342            }
343
344            lrm.add( session, new LocalMetadataRegistration( metadata ) );
345        }
346        catch ( Exception e )
347        {
348            exception = e;
349            throw new InstallationException( "Failed to install metadata " + metadata + ": " + e.getMessage(), e );
350        }
351        finally
352        {
353            metadataInstalled( session, trace, metadata, dstFile, exception );
354        }
355    }
356
357    private void artifactInstalling( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
358                                     File dstFile )
359    {
360        RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_INSTALLING );
361        event.setTrace( trace );
362        event.setArtifact( artifact );
363        event.setRepository( session.getLocalRepositoryManager().getRepository() );
364        event.setFile( dstFile );
365
366        repositoryEventDispatcher.dispatch( event.build() );
367    }
368
369    private void artifactInstalled( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
370                                    File dstFile, Exception exception )
371    {
372        RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_INSTALLED );
373        event.setTrace( trace );
374        event.setArtifact( artifact );
375        event.setRepository( session.getLocalRepositoryManager().getRepository() );
376        event.setFile( dstFile );
377        event.setException( exception );
378
379        repositoryEventDispatcher.dispatch( event.build() );
380    }
381
382    private void metadataInstalling( RepositorySystemSession session, RequestTrace trace, Metadata metadata,
383                                     File dstFile )
384    {
385        RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.METADATA_INSTALLING );
386        event.setTrace( trace );
387        event.setMetadata( metadata );
388        event.setRepository( session.getLocalRepositoryManager().getRepository() );
389        event.setFile( dstFile );
390
391        repositoryEventDispatcher.dispatch( event.build() );
392    }
393
394    private void metadataInstalled( RepositorySystemSession session, RequestTrace trace, Metadata metadata,
395                                    File dstFile, Exception exception )
396    {
397        RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.METADATA_INSTALLED );
398        event.setTrace( trace );
399        event.setMetadata( metadata );
400        event.setRepository( session.getLocalRepositoryManager().getRepository() );
401        event.setFile( dstFile );
402        event.setException( exception );
403
404        repositoryEventDispatcher.dispatch( event.build() );
405    }
406
407}