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