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.nio.file.Path;
026import java.util.*;
027import java.util.ArrayList;
028import java.util.Collections;
029import java.util.IdentityHashMap;
030import java.util.List;
031import java.util.ListIterator;
032import java.util.Map;
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.artifact.generator.ArtifactGenerator;
053import org.eclipse.aether.spi.artifact.generator.ArtifactGeneratorFactory;
054import org.eclipse.aether.spi.io.PathProcessor;
055import org.eclipse.aether.spi.synccontext.SyncContextFactory;
056import org.slf4j.Logger;
057import org.slf4j.LoggerFactory;
058
059import static java.util.Objects.requireNonNull;
060
061/**
062 */
063@Singleton
064@Named
065public class DefaultInstaller implements Installer {
066    private final Logger logger = LoggerFactory.getLogger(getClass());
067
068    private final PathProcessor pathProcessor;
069
070    private final RepositoryEventDispatcher repositoryEventDispatcher;
071
072    private final Map<String, ArtifactGeneratorFactory> artifactFactories;
073
074    private final Map<String, MetadataGeneratorFactory> metadataFactories;
075
076    private final SyncContextFactory syncContextFactory;
077
078    @Inject
079    public DefaultInstaller(
080            PathProcessor pathProcessor,
081            RepositoryEventDispatcher repositoryEventDispatcher,
082            Map<String, ArtifactGeneratorFactory> artifactFactories,
083            Map<String, MetadataGeneratorFactory> metadataFactories,
084            SyncContextFactory syncContextFactory) {
085        this.pathProcessor = requireNonNull(pathProcessor, "path processor cannot be null");
086        this.repositoryEventDispatcher =
087                requireNonNull(repositoryEventDispatcher, "repository event dispatcher cannot be null");
088        this.artifactFactories = Collections.unmodifiableMap(artifactFactories);
089        this.metadataFactories = Collections.unmodifiableMap(metadataFactories);
090        this.syncContextFactory = requireNonNull(syncContextFactory, "sync context factory cannot be null");
091    }
092
093    @Override
094    public InstallResult install(RepositorySystemSession session, InstallRequest request) throws InstallationException {
095        requireNonNull(session, "session cannot be null");
096        requireNonNull(request, "request cannot be null");
097        try (SyncContext syncContext = syncContextFactory.newInstance(session, false)) {
098            return install(syncContext, session, request);
099        }
100    }
101
102    private InstallResult install(SyncContext syncContext, RepositorySystemSession session, InstallRequest request)
103            throws InstallationException {
104        InstallResult result = new InstallResult(request);
105
106        RequestTrace trace = RequestTrace.newChild(request.getTrace(), request);
107
108        List<Artifact> artifacts = new ArrayList<>(request.getArtifacts());
109        List<? extends ArtifactGenerator> artifactGenerators =
110                Utils.getArtifactGenerators(session, artifactFactories, request);
111        try {
112            List<Artifact> generatedArtifacts = new ArrayList<>();
113            for (ArtifactGenerator artifactGenerator : artifactGenerators) {
114                Collection<? extends Artifact> generated = artifactGenerator.generate(generatedArtifacts);
115                for (Artifact generatedArtifact : generated) {
116                    Map<String, String> properties = new HashMap<>(generatedArtifact.getProperties());
117                    properties.put(
118                            ArtifactGeneratorFactory.ARTIFACT_GENERATOR_ID,
119                            requireNonNull(artifactGenerator.generatorId(), "generatorId"));
120                    Artifact ga = generatedArtifact.setProperties(properties);
121                    generatedArtifacts.add(ga);
122                }
123            }
124            artifacts.addAll(generatedArtifacts);
125
126            List<? extends MetadataGenerator> metadataGenerators =
127                    Utils.getMetadataGenerators(session, metadataFactories, request);
128
129            IdentityHashMap<Metadata, Object> processedMetadata = new IdentityHashMap<>();
130
131            List<Metadata> metadatas = Utils.prepareMetadata(metadataGenerators, artifacts);
132
133            syncContext.acquire(artifacts, Utils.combine(request.getMetadata(), metadatas));
134
135            for (Metadata metadata : metadatas) {
136                install(session, trace, metadata);
137                processedMetadata.put(metadata, null);
138                result.addMetadata(metadata);
139            }
140
141            for (ListIterator<Artifact> iterator = artifacts.listIterator(); iterator.hasNext(); ) {
142                Artifact artifact = iterator.next();
143
144                for (MetadataGenerator generator : metadataGenerators) {
145                    artifact = generator.transformArtifact(artifact);
146                }
147
148                iterator.set(artifact);
149
150                install(session, trace, artifact);
151                if (artifact.getProperty(ArtifactGeneratorFactory.ARTIFACT_GENERATOR_ID, null) == null) {
152                    result.addArtifact(artifact);
153                }
154            }
155
156            metadatas = Utils.finishMetadata(metadataGenerators, artifacts);
157
158            syncContext.acquire(null, metadatas);
159
160            for (Metadata metadata : metadatas) {
161                install(session, trace, metadata);
162                processedMetadata.put(metadata, null);
163                result.addMetadata(metadata);
164            }
165
166            for (Metadata metadata : request.getMetadata()) {
167                if (!processedMetadata.containsKey(metadata)) {
168                    install(session, trace, metadata);
169                    result.addMetadata(metadata);
170                }
171            }
172
173            return result;
174        } finally {
175            for (ArtifactGenerator artifactGenerator : artifactGenerators) {
176                try {
177                    artifactGenerator.close();
178                } catch (Exception e) {
179                    logger.warn("ArtifactGenerator close failure: {}", artifactGenerator.generatorId(), e);
180                }
181            }
182        }
183    }
184
185    private void install(RepositorySystemSession session, RequestTrace trace, Artifact artifact)
186            throws InstallationException {
187        final LocalRepositoryManager lrm = session.getLocalRepositoryManager();
188        final Path srcPath = artifact.getPath();
189        final Path dstPath = lrm.getRepository().getBasePath().resolve(lrm.getPathForLocalArtifact(artifact));
190
191        artifactInstalling(session, trace, artifact, dstPath);
192
193        Exception exception = null;
194        try {
195            if (dstPath.equals(srcPath)) {
196                throw new IllegalStateException("cannot install " + dstPath + " to same path");
197            }
198
199            pathProcessor.copyWithTimestamp(srcPath, dstPath);
200            lrm.add(session, new LocalArtifactRegistration(artifact));
201        } catch (Exception e) {
202            exception = e;
203            throw new InstallationException("Failed to install artifact " + artifact + ": " + e.getMessage(), e);
204        } finally {
205            artifactInstalled(session, trace, artifact, dstPath, exception);
206        }
207    }
208
209    private void install(RepositorySystemSession session, RequestTrace trace, Metadata metadata)
210            throws InstallationException {
211        LocalRepositoryManager lrm = session.getLocalRepositoryManager();
212
213        Path dstPath = lrm.getRepository().getBasePath().resolve(lrm.getPathForLocalMetadata(metadata));
214
215        metadataInstalling(session, trace, metadata, dstPath);
216
217        Exception exception = null;
218        try {
219            if (metadata instanceof MergeableMetadata) {
220                ((MergeableMetadata) metadata).merge(dstPath, dstPath);
221            } else {
222                if (dstPath.equals(metadata.getPath())) {
223                    throw new IllegalStateException("cannot install " + dstPath + " to same path");
224                }
225                pathProcessor.copy(metadata.getPath(), dstPath);
226            }
227
228            lrm.add(session, new LocalMetadataRegistration(metadata));
229        } catch (Exception e) {
230            exception = e;
231            throw new InstallationException("Failed to install metadata " + metadata + ": " + e.getMessage(), e);
232        } finally {
233            metadataInstalled(session, trace, metadata, dstPath, exception);
234        }
235    }
236
237    private void artifactInstalling(
238            RepositorySystemSession session, RequestTrace trace, Artifact artifact, Path dstPath) {
239        RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_INSTALLING);
240        event.setTrace(trace);
241        event.setArtifact(artifact);
242        event.setRepository(session.getLocalRepositoryManager().getRepository());
243        event.setPath(dstPath);
244
245        repositoryEventDispatcher.dispatch(event.build());
246    }
247
248    private void artifactInstalled(
249            RepositorySystemSession session, RequestTrace trace, Artifact artifact, Path dstPath, Exception exception) {
250        RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_INSTALLED);
251        event.setTrace(trace);
252        event.setArtifact(artifact);
253        event.setRepository(session.getLocalRepositoryManager().getRepository());
254        event.setPath(dstPath);
255        event.setException(exception);
256
257        repositoryEventDispatcher.dispatch(event.build());
258    }
259
260    private void metadataInstalling(
261            RepositorySystemSession session, RequestTrace trace, Metadata metadata, Path dstPath) {
262        RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_INSTALLING);
263        event.setTrace(trace);
264        event.setMetadata(metadata);
265        event.setRepository(session.getLocalRepositoryManager().getRepository());
266        event.setPath(dstPath);
267
268        repositoryEventDispatcher.dispatch(event.build());
269    }
270
271    private void metadataInstalled(
272            RepositorySystemSession session, RequestTrace trace, Metadata metadata, Path dstPath, Exception exception) {
273        RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_INSTALLED);
274        event.setTrace(trace);
275        event.setMetadata(metadata);
276        event.setRepository(session.getLocalRepositoryManager().getRepository());
277        event.setPath(dstPath);
278        event.setException(exception);
279
280        repositoryEventDispatcher.dispatch(event.build());
281    }
282}