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.Files;
026import java.nio.file.Path;
027import java.util.*;
028import java.util.ArrayList;
029import java.util.Collections;
030import java.util.IdentityHashMap;
031import java.util.List;
032import java.util.ListIterator;
033import java.util.Map;
034
035import org.eclipse.aether.RepositoryEvent;
036import org.eclipse.aether.RepositoryEvent.EventType;
037import org.eclipse.aether.RepositorySystemSession;
038import org.eclipse.aether.RequestTrace;
039import org.eclipse.aether.SyncContext;
040import org.eclipse.aether.artifact.Artifact;
041import org.eclipse.aether.impl.Installer;
042import org.eclipse.aether.impl.MetadataGenerator;
043import org.eclipse.aether.impl.MetadataGeneratorFactory;
044import org.eclipse.aether.impl.RepositoryEventDispatcher;
045import org.eclipse.aether.installation.InstallRequest;
046import org.eclipse.aether.installation.InstallResult;
047import org.eclipse.aether.installation.InstallationException;
048import org.eclipse.aether.metadata.MergeableMetadata;
049import org.eclipse.aether.metadata.Metadata;
050import org.eclipse.aether.repository.LocalArtifactRegistration;
051import org.eclipse.aether.repository.LocalMetadataRegistration;
052import org.eclipse.aether.repository.LocalRepositoryManager;
053import org.eclipse.aether.spi.artifact.generator.ArtifactGenerator;
054import org.eclipse.aether.spi.artifact.generator.ArtifactGeneratorFactory;
055import org.eclipse.aether.spi.io.PathProcessor;
056import org.eclipse.aether.spi.synccontext.SyncContextFactory;
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 {
067    private final Logger logger = LoggerFactory.getLogger(getClass());
068
069    private final PathProcessor pathProcessor;
070
071    private final RepositoryEventDispatcher repositoryEventDispatcher;
072
073    private final Map<String, ArtifactGeneratorFactory> artifactFactories;
074
075    private final Map<String, MetadataGeneratorFactory> metadataFactories;
076
077    private final SyncContextFactory syncContextFactory;
078
079    @Inject
080    public DefaultInstaller(
081            PathProcessor pathProcessor,
082            RepositoryEventDispatcher repositoryEventDispatcher,
083            Map<String, ArtifactGeneratorFactory> artifactFactories,
084            Map<String, MetadataGeneratorFactory> metadataFactories,
085            SyncContextFactory syncContextFactory) {
086        this.pathProcessor = requireNonNull(pathProcessor, "path processor cannot be null");
087        this.repositoryEventDispatcher =
088                requireNonNull(repositoryEventDispatcher, "repository event dispatcher cannot be null");
089        this.artifactFactories = Collections.unmodifiableMap(artifactFactories);
090        this.metadataFactories = Collections.unmodifiableMap(metadataFactories);
091        this.syncContextFactory = requireNonNull(syncContextFactory, "sync context factory cannot be null");
092    }
093
094    @Override
095    public InstallResult install(RepositorySystemSession session, InstallRequest request) throws InstallationException {
096        requireNonNull(session, "session cannot be null");
097        requireNonNull(request, "request cannot be null");
098        try (SyncContext syncContext = syncContextFactory.newInstance(session, false)) {
099            return install(syncContext, session, request);
100        }
101    }
102
103    private InstallResult install(SyncContext syncContext, RepositorySystemSession session, InstallRequest request)
104            throws InstallationException {
105        InstallResult result = new InstallResult(request);
106
107        RequestTrace trace = RequestTrace.newChild(request.getTrace(), request);
108
109        List<Artifact> artifacts = new ArrayList<>(request.getArtifacts());
110        List<? extends ArtifactGenerator> artifactGenerators = getArtifactGenerators(session, 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 = getMetadataGenerators(session, request);
127
128            IdentityHashMap<Metadata, Object> processedMetadata = new IdentityHashMap<>();
129
130            List<Metadata> metadatas = Utils.prepareMetadata(metadataGenerators, artifacts);
131
132            syncContext.acquire(artifacts, Utils.combine(request.getMetadata(), metadatas));
133
134            for (Metadata metadata : metadatas) {
135                install(session, trace, metadata);
136                processedMetadata.put(metadata, null);
137                result.addMetadata(metadata);
138            }
139
140            for (ListIterator<Artifact> iterator = artifacts.listIterator(); iterator.hasNext(); ) {
141                Artifact artifact = iterator.next();
142
143                for (MetadataGenerator generator : metadataGenerators) {
144                    artifact = generator.transformArtifact(artifact);
145                }
146
147                iterator.set(artifact);
148
149                install(session, trace, artifact);
150                if (artifact.getProperty(ArtifactGeneratorFactory.ARTIFACT_GENERATOR_ID, null) == null) {
151                    result.addArtifact(artifact);
152                }
153            }
154
155            metadatas = Utils.finishMetadata(metadataGenerators, artifacts);
156
157            syncContext.acquire(null, metadatas);
158
159            for (Metadata metadata : metadatas) {
160                install(session, trace, metadata);
161                processedMetadata.put(metadata, null);
162                result.addMetadata(metadata);
163            }
164
165            for (Metadata metadata : request.getMetadata()) {
166                if (!processedMetadata.containsKey(metadata)) {
167                    install(session, trace, metadata);
168                    result.addMetadata(metadata);
169                }
170            }
171
172            return result;
173        } finally {
174            for (ArtifactGenerator artifactGenerator : artifactGenerators) {
175                try {
176                    artifactGenerator.close();
177                } catch (Exception e) {
178                    logger.warn("ArtifactGenerator close failure: {}", artifactGenerator.generatorId(), e);
179                }
180            }
181        }
182    }
183
184    private List<? extends ArtifactGenerator> getArtifactGenerators(
185            RepositorySystemSession session, InstallRequest request) {
186        PrioritizedComponents<ArtifactGeneratorFactory> factories =
187                Utils.sortArtifactGeneratorFactories(session, artifactFactories);
188
189        List<ArtifactGenerator> generators = new ArrayList<>();
190
191        for (PrioritizedComponent<ArtifactGeneratorFactory> factory : factories.getEnabled()) {
192            ArtifactGenerator generator = factory.getComponent().newInstance(session, request);
193            if (generator != null) {
194                generators.add(generator);
195            }
196        }
197
198        return generators;
199    }
200
201    private List<? extends MetadataGenerator> getMetadataGenerators(
202            RepositorySystemSession session, InstallRequest request) {
203        PrioritizedComponents<MetadataGeneratorFactory> factories =
204                Utils.sortMetadataGeneratorFactories(session, metadataFactories);
205
206        List<MetadataGenerator> generators = new ArrayList<>();
207
208        for (PrioritizedComponent<MetadataGeneratorFactory> factory : factories.getEnabled()) {
209            MetadataGenerator generator = factory.getComponent().newInstance(session, request);
210            if (generator != null) {
211                generators.add(generator);
212            }
213        }
214
215        return generators;
216    }
217
218    private void install(RepositorySystemSession session, RequestTrace trace, Artifact artifact)
219            throws InstallationException {
220        final LocalRepositoryManager lrm = session.getLocalRepositoryManager();
221        final Path srcPath = artifact.getPath();
222        final Path dstPath = lrm.getRepository().getBasePath().resolve(lrm.getPathForLocalArtifact(artifact));
223
224        artifactInstalling(session, trace, artifact, dstPath);
225
226        Exception exception = null;
227        try {
228            if (dstPath.equals(srcPath)) {
229                throw new IllegalStateException("cannot install " + dstPath + " to same path");
230            }
231
232            pathProcessor.copy(srcPath, dstPath);
233            Files.setLastModifiedTime(dstPath, Files.getLastModifiedTime(srcPath));
234            lrm.add(session, new LocalArtifactRegistration(artifact));
235        } catch (Exception e) {
236            exception = e;
237            throw new InstallationException("Failed to install artifact " + artifact + ": " + e.getMessage(), e);
238        } finally {
239            artifactInstalled(session, trace, artifact, dstPath, exception);
240        }
241    }
242
243    private void install(RepositorySystemSession session, RequestTrace trace, Metadata metadata)
244            throws InstallationException {
245        LocalRepositoryManager lrm = session.getLocalRepositoryManager();
246
247        Path dstPath = lrm.getRepository().getBasePath().resolve(lrm.getPathForLocalMetadata(metadata));
248
249        metadataInstalling(session, trace, metadata, dstPath);
250
251        Exception exception = null;
252        try {
253            if (metadata instanceof MergeableMetadata) {
254                ((MergeableMetadata) metadata).merge(dstPath, dstPath);
255            } else {
256                if (dstPath.equals(metadata.getPath())) {
257                    throw new IllegalStateException("cannot install " + dstPath + " to same path");
258                }
259                pathProcessor.copy(metadata.getPath(), dstPath);
260            }
261
262            lrm.add(session, new LocalMetadataRegistration(metadata));
263        } catch (Exception e) {
264            exception = e;
265            throw new InstallationException("Failed to install metadata " + metadata + ": " + e.getMessage(), e);
266        } finally {
267            metadataInstalled(session, trace, metadata, dstPath, exception);
268        }
269    }
270
271    private void artifactInstalling(
272            RepositorySystemSession session, RequestTrace trace, Artifact artifact, Path dstPath) {
273        RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_INSTALLING);
274        event.setTrace(trace);
275        event.setArtifact(artifact);
276        event.setRepository(session.getLocalRepositoryManager().getRepository());
277        event.setPath(dstPath);
278
279        repositoryEventDispatcher.dispatch(event.build());
280    }
281
282    private void artifactInstalled(
283            RepositorySystemSession session, RequestTrace trace, Artifact artifact, Path dstPath, Exception exception) {
284        RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_INSTALLED);
285        event.setTrace(trace);
286        event.setArtifact(artifact);
287        event.setRepository(session.getLocalRepositoryManager().getRepository());
288        event.setPath(dstPath);
289        event.setException(exception);
290
291        repositoryEventDispatcher.dispatch(event.build());
292    }
293
294    private void metadataInstalling(
295            RepositorySystemSession session, RequestTrace trace, Metadata metadata, Path dstPath) {
296        RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_INSTALLING);
297        event.setTrace(trace);
298        event.setMetadata(metadata);
299        event.setRepository(session.getLocalRepositoryManager().getRepository());
300        event.setPath(dstPath);
301
302        repositoryEventDispatcher.dispatch(event.build());
303    }
304
305    private void metadataInstalled(
306            RepositorySystemSession session, RequestTrace trace, Metadata metadata, Path dstPath, Exception exception) {
307        RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_INSTALLED);
308        event.setTrace(trace);
309        event.setMetadata(metadata);
310        event.setRepository(session.getLocalRepositoryManager().getRepository());
311        event.setPath(dstPath);
312        event.setException(exception);
313
314        repositoryEventDispatcher.dispatch(event.build());
315    }
316}