001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.eclipse.aether.internal.impl;
020
021import javax.inject.Inject;
022import javax.inject.Named;
023import javax.inject.Singleton;
024
025import java.io.File;
026import java.io.InputStream;
027import java.util.ArrayList;
028import java.util.Collection;
029import java.util.IdentityHashMap;
030import java.util.List;
031import java.util.ListIterator;
032import java.util.Set;
033
034import org.eclipse.aether.RepositoryEvent;
035import org.eclipse.aether.RepositoryEvent.EventType;
036import org.eclipse.aether.RepositorySystemSession;
037import org.eclipse.aether.RequestTrace;
038import org.eclipse.aether.SyncContext;
039import org.eclipse.aether.artifact.Artifact;
040import org.eclipse.aether.impl.Installer;
041import org.eclipse.aether.impl.MetadataGenerator;
042import org.eclipse.aether.impl.MetadataGeneratorFactory;
043import org.eclipse.aether.impl.RepositoryEventDispatcher;
044import org.eclipse.aether.installation.InstallRequest;
045import org.eclipse.aether.installation.InstallResult;
046import org.eclipse.aether.installation.InstallationException;
047import org.eclipse.aether.metadata.MergeableMetadata;
048import org.eclipse.aether.metadata.Metadata;
049import org.eclipse.aether.repository.LocalArtifactRegistration;
050import org.eclipse.aether.repository.LocalMetadataRegistration;
051import org.eclipse.aether.repository.LocalRepositoryManager;
052import org.eclipse.aether.spi.io.FileProcessor;
053import org.eclipse.aether.spi.locator.Service;
054import org.eclipse.aether.spi.locator.ServiceLocator;
055import org.eclipse.aether.spi.synccontext.SyncContextFactory;
056import org.eclipse.aether.transform.FileTransformer;
057import org.slf4j.Logger;
058import org.slf4j.LoggerFactory;
059
060import static java.util.Objects.requireNonNull;
061
062/**
063 */
064@Singleton
065@Named
066public class DefaultInstaller implements Installer, Service {
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<>();
075
076    private SyncContextFactory syncContextFactory;
077
078    @Deprecated
079    public DefaultInstaller() {
080        // enables default constructor
081    }
082
083    @Inject
084    public DefaultInstaller(
085            FileProcessor fileProcessor,
086            RepositoryEventDispatcher repositoryEventDispatcher,
087            Set<MetadataGeneratorFactory> metadataFactories,
088            SyncContextFactory syncContextFactory) {
089        setFileProcessor(fileProcessor);
090        setRepositoryEventDispatcher(repositoryEventDispatcher);
091        setMetadataGeneratorFactories(metadataFactories);
092        setSyncContextFactory(syncContextFactory);
093    }
094
095    public void initService(ServiceLocator locator) {
096        setFileProcessor(locator.getService(FileProcessor.class));
097        setRepositoryEventDispatcher(locator.getService(RepositoryEventDispatcher.class));
098        setMetadataGeneratorFactories(locator.getServices(MetadataGeneratorFactory.class));
099        setSyncContextFactory(locator.getService(SyncContextFactory.class));
100    }
101
102    public DefaultInstaller setFileProcessor(FileProcessor fileProcessor) {
103        this.fileProcessor = requireNonNull(fileProcessor, "file processor cannot be null");
104        return this;
105    }
106
107    public DefaultInstaller setRepositoryEventDispatcher(RepositoryEventDispatcher repositoryEventDispatcher) {
108        this.repositoryEventDispatcher =
109                requireNonNull(repositoryEventDispatcher, "repository event dispatcher cannot be null");
110        return this;
111    }
112
113    public DefaultInstaller addMetadataGeneratorFactory(MetadataGeneratorFactory factory) {
114        metadataFactories.add(requireNonNull(factory, "metadata generator factory cannot be null"));
115        return this;
116    }
117
118    public DefaultInstaller setMetadataGeneratorFactories(Collection<MetadataGeneratorFactory> metadataFactories) {
119        if (metadataFactories == null) {
120            this.metadataFactories = new ArrayList<>();
121        } else {
122            this.metadataFactories = metadataFactories;
123        }
124        return this;
125    }
126
127    public DefaultInstaller setSyncContextFactory(SyncContextFactory syncContextFactory) {
128        this.syncContextFactory = requireNonNull(syncContextFactory, "sync context factory cannot be null");
129        return this;
130    }
131
132    public InstallResult install(RepositorySystemSession session, InstallRequest request) throws InstallationException {
133        requireNonNull(session, "session cannot be null");
134        requireNonNull(request, "request cannot be null");
135        try (SyncContext syncContext = syncContextFactory.newInstance(session, false)) {
136            return install(syncContext, session, request);
137        }
138    }
139
140    private InstallResult install(SyncContext syncContext, RepositorySystemSession session, InstallRequest request)
141            throws InstallationException {
142        InstallResult result = new InstallResult(request);
143
144        RequestTrace trace = RequestTrace.newChild(request.getTrace(), request);
145
146        List<? extends MetadataGenerator> generators = getMetadataGenerators(session, request);
147
148        List<Artifact> artifacts = new ArrayList<>(request.getArtifacts());
149
150        IdentityHashMap<Metadata, Object> processedMetadata = new IdentityHashMap<>();
151
152        List<Metadata> metadatas = Utils.prepareMetadata(generators, artifacts);
153
154        syncContext.acquire(artifacts, Utils.combine(request.getMetadata(), metadatas));
155
156        for (Metadata metadata : metadatas) {
157            install(session, trace, metadata);
158            processedMetadata.put(metadata, null);
159            result.addMetadata(metadata);
160        }
161
162        for (ListIterator<Artifact> iterator = artifacts.listIterator(); iterator.hasNext(); ) {
163            Artifact artifact = iterator.next();
164
165            for (MetadataGenerator generator : generators) {
166                artifact = generator.transformArtifact(artifact);
167            }
168
169            iterator.set(artifact);
170
171            install(session, trace, artifact);
172            result.addArtifact(artifact);
173        }
174
175        metadatas = Utils.finishMetadata(generators, artifacts);
176
177        syncContext.acquire(null, metadatas);
178
179        for (Metadata metadata : metadatas) {
180            install(session, trace, metadata);
181            processedMetadata.put(metadata, null);
182            result.addMetadata(metadata);
183        }
184
185        for (Metadata metadata : request.getMetadata()) {
186            if (!processedMetadata.containsKey(metadata)) {
187                install(session, trace, metadata);
188                result.addMetadata(metadata);
189            }
190        }
191
192        return result;
193    }
194
195    private List<? extends MetadataGenerator> getMetadataGenerators(
196            RepositorySystemSession session, InstallRequest request) {
197        PrioritizedComponents<MetadataGeneratorFactory> factories =
198                Utils.sortMetadataGeneratorFactories(session, this.metadataFactories);
199
200        List<MetadataGenerator> generators = new ArrayList<>();
201
202        for (PrioritizedComponent<MetadataGeneratorFactory> factory : factories.getEnabled()) {
203            MetadataGenerator generator = factory.getComponent().newInstance(session, request);
204            if (generator != null) {
205                generators.add(generator);
206            }
207        }
208
209        return generators;
210    }
211
212    private void install(RepositorySystemSession session, RequestTrace trace, Artifact artifact)
213            throws InstallationException {
214        LocalRepositoryManager lrm = session.getLocalRepositoryManager();
215
216        File srcFile = artifact.getFile();
217
218        Collection<FileTransformer> fileTransformers =
219                session.getFileTransformerManager().getTransformersForArtifact(artifact);
220        if (fileTransformers.isEmpty()) {
221            install(session, trace, artifact, lrm, srcFile, null);
222        } else {
223            for (FileTransformer fileTransformer : fileTransformers) {
224                install(session, trace, artifact, lrm, srcFile, fileTransformer);
225            }
226        }
227    }
228
229    private void install(
230            RepositorySystemSession session,
231            RequestTrace trace,
232            Artifact artifact,
233            LocalRepositoryManager lrm,
234            File srcFile,
235            FileTransformer fileTransformer)
236            throws InstallationException {
237        final Artifact targetArtifact;
238        if (fileTransformer != null) {
239            targetArtifact = fileTransformer.transformArtifact(artifact);
240        } else {
241            targetArtifact = artifact;
242        }
243
244        File dstFile = new File(lrm.getRepository().getBasedir(), lrm.getPathForLocalArtifact(targetArtifact));
245
246        artifactInstalling(session, trace, targetArtifact, dstFile);
247
248        Exception exception = null;
249        try {
250            if (dstFile.equals(srcFile)) {
251                throw new IllegalStateException("cannot install " + dstFile + " to same path");
252            }
253
254            boolean copy = "pom".equals(targetArtifact.getExtension())
255                    || srcFile.lastModified() != dstFile.lastModified()
256                    || srcFile.length() != dstFile.length()
257                    || !srcFile.exists();
258
259            if (!copy) {
260                LOGGER.debug("Skipped re-installing {} to {}, seems unchanged", srcFile, dstFile);
261            } else if (fileTransformer != null) {
262                try (InputStream is = fileTransformer.transformData(srcFile)) {
263                    fileProcessor.write(dstFile, is);
264                    dstFile.setLastModified(srcFile.lastModified());
265                }
266            } else {
267                fileProcessor.copy(srcFile, dstFile);
268                dstFile.setLastModified(srcFile.lastModified());
269            }
270
271            lrm.add(session, new LocalArtifactRegistration(targetArtifact));
272        } catch (Exception e) {
273            exception = e;
274            throw new InstallationException("Failed to install artifact " + targetArtifact + ": " + e.getMessage(), e);
275        } finally {
276            artifactInstalled(session, trace, targetArtifact, dstFile, exception);
277        }
278    }
279
280    private void install(RepositorySystemSession session, RequestTrace trace, Metadata metadata)
281            throws InstallationException {
282        LocalRepositoryManager lrm = session.getLocalRepositoryManager();
283
284        File dstFile = new File(lrm.getRepository().getBasedir(), lrm.getPathForLocalMetadata(metadata));
285
286        metadataInstalling(session, trace, metadata, dstFile);
287
288        Exception exception = null;
289        try {
290            if (metadata instanceof MergeableMetadata) {
291                ((MergeableMetadata) metadata).merge(dstFile, dstFile);
292            } else {
293                if (dstFile.equals(metadata.getFile())) {
294                    throw new IllegalStateException("cannot install " + dstFile + " to same path");
295                }
296                fileProcessor.copy(metadata.getFile(), dstFile);
297            }
298
299            lrm.add(session, new LocalMetadataRegistration(metadata));
300        } catch (Exception e) {
301            exception = e;
302            throw new InstallationException("Failed to install metadata " + metadata + ": " + e.getMessage(), e);
303        } finally {
304            metadataInstalled(session, trace, metadata, dstFile, exception);
305        }
306    }
307
308    private void artifactInstalling(
309            RepositorySystemSession session, RequestTrace trace, Artifact artifact, File dstFile) {
310        RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_INSTALLING);
311        event.setTrace(trace);
312        event.setArtifact(artifact);
313        event.setRepository(session.getLocalRepositoryManager().getRepository());
314        event.setFile(dstFile);
315
316        repositoryEventDispatcher.dispatch(event.build());
317    }
318
319    private void artifactInstalled(
320            RepositorySystemSession session, RequestTrace trace, Artifact artifact, File dstFile, Exception exception) {
321        RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_INSTALLED);
322        event.setTrace(trace);
323        event.setArtifact(artifact);
324        event.setRepository(session.getLocalRepositoryManager().getRepository());
325        event.setFile(dstFile);
326        event.setException(exception);
327
328        repositoryEventDispatcher.dispatch(event.build());
329    }
330
331    private void metadataInstalling(
332            RepositorySystemSession session, RequestTrace trace, Metadata metadata, File dstFile) {
333        RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_INSTALLING);
334        event.setTrace(trace);
335        event.setMetadata(metadata);
336        event.setRepository(session.getLocalRepositoryManager().getRepository());
337        event.setFile(dstFile);
338
339        repositoryEventDispatcher.dispatch(event.build());
340    }
341
342    private void metadataInstalled(
343            RepositorySystemSession session, RequestTrace trace, Metadata metadata, File dstFile, Exception exception) {
344        RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_INSTALLED);
345        event.setTrace(trace);
346        event.setMetadata(metadata);
347        event.setRepository(session.getLocalRepositoryManager().getRepository());
348        event.setFile(dstFile);
349        event.setException(exception);
350
351        repositoryEventDispatcher.dispatch(event.build());
352    }
353}