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    public DefaultInstaller() {
079        // enables default constructor
080    }
081
082    @Inject
083    DefaultInstaller(
084            FileProcessor fileProcessor,
085            RepositoryEventDispatcher repositoryEventDispatcher,
086            Set<MetadataGeneratorFactory> metadataFactories,
087            SyncContextFactory syncContextFactory) {
088        setFileProcessor(fileProcessor);
089        setRepositoryEventDispatcher(repositoryEventDispatcher);
090        setMetadataGeneratorFactories(metadataFactories);
091        setSyncContextFactory(syncContextFactory);
092    }
093
094    public void initService(ServiceLocator locator) {
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        this.fileProcessor = requireNonNull(fileProcessor, "file processor cannot be null");
103        return this;
104    }
105
106    public DefaultInstaller setRepositoryEventDispatcher(RepositoryEventDispatcher repositoryEventDispatcher) {
107        this.repositoryEventDispatcher =
108                requireNonNull(repositoryEventDispatcher, "repository event dispatcher cannot be null");
109        return this;
110    }
111
112    public DefaultInstaller addMetadataGeneratorFactory(MetadataGeneratorFactory factory) {
113        metadataFactories.add(requireNonNull(factory, "metadata generator factory cannot be null"));
114        return this;
115    }
116
117    public DefaultInstaller setMetadataGeneratorFactories(Collection<MetadataGeneratorFactory> metadataFactories) {
118        if (metadataFactories == null) {
119            this.metadataFactories = new ArrayList<>();
120        } else {
121            this.metadataFactories = metadataFactories;
122        }
123        return this;
124    }
125
126    public DefaultInstaller setSyncContextFactory(SyncContextFactory syncContextFactory) {
127        this.syncContextFactory = requireNonNull(syncContextFactory, "sync context factory cannot be null");
128        return this;
129    }
130
131    public InstallResult install(RepositorySystemSession session, InstallRequest request) throws InstallationException {
132        requireNonNull(session, "session cannot be null");
133        requireNonNull(request, "request cannot be null");
134        try (SyncContext syncContext = syncContextFactory.newInstance(session, false)) {
135            return install(syncContext, session, request);
136        }
137    }
138
139    private InstallResult install(SyncContext syncContext, RepositorySystemSession session, InstallRequest request)
140            throws InstallationException {
141        InstallResult result = new InstallResult(request);
142
143        RequestTrace trace = RequestTrace.newChild(request.getTrace(), request);
144
145        List<? extends MetadataGenerator> generators = getMetadataGenerators(session, request);
146
147        List<Artifact> artifacts = new ArrayList<>(request.getArtifacts());
148
149        IdentityHashMap<Metadata, Object> processedMetadata = new IdentityHashMap<>();
150
151        List<Metadata> metadatas = Utils.prepareMetadata(generators, artifacts);
152
153        syncContext.acquire(artifacts, Utils.combine(request.getMetadata(), metadatas));
154
155        for (Metadata metadata : metadatas) {
156            install(session, trace, metadata);
157            processedMetadata.put(metadata, null);
158            result.addMetadata(metadata);
159        }
160
161        for (ListIterator<Artifact> iterator = artifacts.listIterator(); iterator.hasNext(); ) {
162            Artifact artifact = iterator.next();
163
164            for (MetadataGenerator generator : generators) {
165                artifact = generator.transformArtifact(artifact);
166            }
167
168            iterator.set(artifact);
169
170            install(session, trace, artifact);
171            result.addArtifact(artifact);
172        }
173
174        metadatas = Utils.finishMetadata(generators, artifacts);
175
176        syncContext.acquire(null, metadatas);
177
178        for (Metadata metadata : metadatas) {
179            install(session, trace, metadata);
180            processedMetadata.put(metadata, null);
181            result.addMetadata(metadata);
182        }
183
184        for (Metadata metadata : request.getMetadata()) {
185            if (!processedMetadata.containsKey(metadata)) {
186                install(session, trace, metadata);
187                result.addMetadata(metadata);
188            }
189        }
190
191        return result;
192    }
193
194    private List<? extends MetadataGenerator> getMetadataGenerators(
195            RepositorySystemSession session, InstallRequest request) {
196        PrioritizedComponents<MetadataGeneratorFactory> factories =
197                Utils.sortMetadataGeneratorFactories(session, this.metadataFactories);
198
199        List<MetadataGenerator> generators = new ArrayList<>();
200
201        for (PrioritizedComponent<MetadataGeneratorFactory> factory : factories.getEnabled()) {
202            MetadataGenerator generator = factory.getComponent().newInstance(session, request);
203            if (generator != null) {
204                generators.add(generator);
205            }
206        }
207
208        return generators;
209    }
210
211    private void install(RepositorySystemSession session, RequestTrace trace, Artifact artifact)
212            throws InstallationException {
213        LocalRepositoryManager lrm = session.getLocalRepositoryManager();
214
215        File srcFile = artifact.getFile();
216
217        Collection<FileTransformer> fileTransformers =
218                session.getFileTransformerManager().getTransformersForArtifact(artifact);
219        if (fileTransformers.isEmpty()) {
220            install(session, trace, artifact, lrm, srcFile, null);
221        } else {
222            for (FileTransformer fileTransformer : fileTransformers) {
223                install(session, trace, artifact, lrm, srcFile, fileTransformer);
224            }
225        }
226    }
227
228    private void install(
229            RepositorySystemSession session,
230            RequestTrace trace,
231            Artifact artifact,
232            LocalRepositoryManager lrm,
233            File srcFile,
234            FileTransformer fileTransformer)
235            throws InstallationException {
236        final Artifact targetArtifact;
237        if (fileTransformer != null) {
238            targetArtifact = fileTransformer.transformArtifact(artifact);
239        } else {
240            targetArtifact = artifact;
241        }
242
243        File dstFile = new File(lrm.getRepository().getBasedir(), lrm.getPathForLocalArtifact(targetArtifact));
244
245        artifactInstalling(session, trace, targetArtifact, dstFile);
246
247        Exception exception = null;
248        try {
249            if (dstFile.equals(srcFile)) {
250                throw new IllegalStateException("cannot install " + dstFile + " to same path");
251            }
252
253            boolean copy = "pom".equals(targetArtifact.getExtension())
254                    || srcFile.lastModified() != dstFile.lastModified()
255                    || srcFile.length() != dstFile.length()
256                    || !srcFile.exists();
257
258            if (!copy) {
259                LOGGER.debug("Skipped re-installing {} to {}, seems unchanged", srcFile, dstFile);
260            } else if (fileTransformer != null) {
261                try (InputStream is = fileTransformer.transformData(srcFile)) {
262                    fileProcessor.write(dstFile, is);
263                    dstFile.setLastModified(srcFile.lastModified());
264                }
265            } else {
266                fileProcessor.copy(srcFile, dstFile);
267                dstFile.setLastModified(srcFile.lastModified());
268            }
269
270            lrm.add(session, new LocalArtifactRegistration(targetArtifact));
271        } catch (Exception e) {
272            exception = e;
273            throw new InstallationException("Failed to install artifact " + targetArtifact + ": " + e.getMessage(), e);
274        } finally {
275            artifactInstalled(session, trace, targetArtifact, dstFile, exception);
276        }
277    }
278
279    private void install(RepositorySystemSession session, RequestTrace trace, Metadata metadata)
280            throws InstallationException {
281        LocalRepositoryManager lrm = session.getLocalRepositoryManager();
282
283        File dstFile = new File(lrm.getRepository().getBasedir(), lrm.getPathForLocalMetadata(metadata));
284
285        metadataInstalling(session, trace, metadata, dstFile);
286
287        Exception exception = null;
288        try {
289            if (metadata instanceof MergeableMetadata) {
290                ((MergeableMetadata) metadata).merge(dstFile, dstFile);
291            } else {
292                if (dstFile.equals(metadata.getFile())) {
293                    throw new IllegalStateException("cannot install " + dstFile + " to same path");
294                }
295                fileProcessor.copy(metadata.getFile(), dstFile);
296            }
297
298            lrm.add(session, new LocalMetadataRegistration(metadata));
299        } catch (Exception e) {
300            exception = e;
301            throw new InstallationException("Failed to install metadata " + metadata + ": " + e.getMessage(), e);
302        } finally {
303            metadataInstalled(session, trace, metadata, dstFile, exception);
304        }
305    }
306
307    private void artifactInstalling(
308            RepositorySystemSession session, RequestTrace trace, Artifact artifact, File dstFile) {
309        RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_INSTALLING);
310        event.setTrace(trace);
311        event.setArtifact(artifact);
312        event.setRepository(session.getLocalRepositoryManager().getRepository());
313        event.setFile(dstFile);
314
315        repositoryEventDispatcher.dispatch(event.build());
316    }
317
318    private void artifactInstalled(
319            RepositorySystemSession session, RequestTrace trace, Artifact artifact, File dstFile, Exception exception) {
320        RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_INSTALLED);
321        event.setTrace(trace);
322        event.setArtifact(artifact);
323        event.setRepository(session.getLocalRepositoryManager().getRepository());
324        event.setFile(dstFile);
325        event.setException(exception);
326
327        repositoryEventDispatcher.dispatch(event.build());
328    }
329
330    private void metadataInstalling(
331            RepositorySystemSession session, RequestTrace trace, Metadata metadata, File dstFile) {
332        RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_INSTALLING);
333        event.setTrace(trace);
334        event.setMetadata(metadata);
335        event.setRepository(session.getLocalRepositoryManager().getRepository());
336        event.setFile(dstFile);
337
338        repositoryEventDispatcher.dispatch(event.build());
339    }
340
341    private void metadataInstalled(
342            RepositorySystemSession session, RequestTrace trace, Metadata metadata, File dstFile, Exception exception) {
343        RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_INSTALLED);
344        event.setTrace(trace);
345        event.setMetadata(metadata);
346        event.setRepository(session.getLocalRepositoryManager().getRepository());
347        event.setFile(dstFile);
348        event.setException(exception);
349
350        repositoryEventDispatcher.dispatch(event.build());
351    }
352}