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