001/*
002 * Copyright 2012-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.boot.cli.compiler.maven;
018
019import java.io.BufferedReader;
020import java.io.File;
021import java.io.PrintWriter;
022import java.io.StringReader;
023import java.io.StringWriter;
024import java.util.ArrayList;
025import java.util.Collections;
026import java.util.List;
027import java.util.Map;
028
029import org.apache.maven.model.ActivationFile;
030import org.apache.maven.model.ActivationOS;
031import org.apache.maven.model.ActivationProperty;
032import org.apache.maven.model.building.ModelProblemCollector;
033import org.apache.maven.model.building.ModelProblemCollectorRequest;
034import org.apache.maven.model.path.DefaultPathTranslator;
035import org.apache.maven.model.profile.DefaultProfileSelector;
036import org.apache.maven.model.profile.ProfileActivationContext;
037import org.apache.maven.model.profile.activation.FileProfileActivator;
038import org.apache.maven.model.profile.activation.JdkVersionProfileActivator;
039import org.apache.maven.model.profile.activation.OperatingSystemProfileActivator;
040import org.apache.maven.model.profile.activation.PropertyProfileActivator;
041import org.apache.maven.settings.Activation;
042import org.apache.maven.settings.Mirror;
043import org.apache.maven.settings.Profile;
044import org.apache.maven.settings.Proxy;
045import org.apache.maven.settings.Server;
046import org.apache.maven.settings.Settings;
047import org.apache.maven.settings.crypto.SettingsDecryptionResult;
048import org.eclipse.aether.repository.Authentication;
049import org.eclipse.aether.repository.AuthenticationSelector;
050import org.eclipse.aether.repository.MirrorSelector;
051import org.eclipse.aether.repository.ProxySelector;
052import org.eclipse.aether.util.repository.AuthenticationBuilder;
053import org.eclipse.aether.util.repository.ConservativeAuthenticationSelector;
054import org.eclipse.aether.util.repository.DefaultAuthenticationSelector;
055import org.eclipse.aether.util.repository.DefaultMirrorSelector;
056import org.eclipse.aether.util.repository.DefaultProxySelector;
057
058/**
059 * An encapsulation of settings read from a user's Maven settings.xml.
060 *
061 * @author Andy Wilkinson
062 * @since 1.3.0
063 * @see MavenSettingsReader
064 */
065public class MavenSettings {
066
067        private final boolean offline;
068
069        private final MirrorSelector mirrorSelector;
070
071        private final AuthenticationSelector authenticationSelector;
072
073        private final ProxySelector proxySelector;
074
075        private final String localRepository;
076
077        private final List<Profile> activeProfiles;
078
079        /**
080         * Create a new {@link MavenSettings} instance.
081         * @param settings the source settings
082         * @param decryptedSettings the decrypted settings
083         */
084        public MavenSettings(Settings settings, SettingsDecryptionResult decryptedSettings) {
085                this.offline = settings.isOffline();
086                this.mirrorSelector = createMirrorSelector(settings);
087                this.authenticationSelector = createAuthenticationSelector(decryptedSettings);
088                this.proxySelector = createProxySelector(decryptedSettings);
089                this.localRepository = settings.getLocalRepository();
090                this.activeProfiles = determineActiveProfiles(settings);
091        }
092
093        private MirrorSelector createMirrorSelector(Settings settings) {
094                DefaultMirrorSelector selector = new DefaultMirrorSelector();
095                for (Mirror mirror : settings.getMirrors()) {
096                        selector.add(mirror.getId(), mirror.getUrl(), mirror.getLayout(), false,
097                                        mirror.getMirrorOf(), mirror.getMirrorOfLayouts());
098                }
099                return selector;
100        }
101
102        private AuthenticationSelector createAuthenticationSelector(
103                        SettingsDecryptionResult decryptedSettings) {
104                DefaultAuthenticationSelector selector = new DefaultAuthenticationSelector();
105                for (Server server : decryptedSettings.getServers()) {
106                        AuthenticationBuilder auth = new AuthenticationBuilder();
107                        auth.addUsername(server.getUsername()).addPassword(server.getPassword());
108                        auth.addPrivateKey(server.getPrivateKey(), server.getPassphrase());
109                        selector.add(server.getId(), auth.build());
110                }
111                return new ConservativeAuthenticationSelector(selector);
112        }
113
114        private ProxySelector createProxySelector(
115                        SettingsDecryptionResult decryptedSettings) {
116                DefaultProxySelector selector = new DefaultProxySelector();
117                for (Proxy proxy : decryptedSettings.getProxies()) {
118                        Authentication authentication = new AuthenticationBuilder()
119                                        .addUsername(proxy.getUsername()).addPassword(proxy.getPassword())
120                                        .build();
121                        selector.add(
122                                        new org.eclipse.aether.repository.Proxy(proxy.getProtocol(),
123                                                        proxy.getHost(), proxy.getPort(), authentication),
124                                        proxy.getNonProxyHosts());
125                }
126                return selector;
127        }
128
129        private List<Profile> determineActiveProfiles(Settings settings) {
130                SpringBootCliModelProblemCollector problemCollector = new SpringBootCliModelProblemCollector();
131                List<org.apache.maven.model.Profile> activeModelProfiles = createProfileSelector()
132                                .getActiveProfiles(createModelProfiles(settings.getProfiles()),
133                                                new SpringBootCliProfileActivationContext(
134                                                                settings.getActiveProfiles()),
135                                                problemCollector);
136                if (!problemCollector.getProblems().isEmpty()) {
137                        throw new IllegalStateException(createFailureMessage(problemCollector));
138                }
139                List<Profile> activeProfiles = new ArrayList<>();
140                Map<String, Profile> profiles = settings.getProfilesAsMap();
141                for (org.apache.maven.model.Profile modelProfile : activeModelProfiles) {
142                        activeProfiles.add(profiles.get(modelProfile.getId()));
143                }
144                return activeProfiles;
145        }
146
147        private String createFailureMessage(
148                        SpringBootCliModelProblemCollector problemCollector) {
149                StringWriter message = new StringWriter();
150                PrintWriter printer = new PrintWriter(message);
151                printer.println("Failed to determine active profiles:");
152                for (ModelProblemCollectorRequest problem : problemCollector.getProblems()) {
153                        String location = (problem.getLocation() != null)
154                                        ? " at " + problem.getLocation() : "";
155                        printer.println("    " + problem.getMessage() + location);
156                        if (problem.getException() != null) {
157                                printer.println(indentStackTrace(problem.getException(), "        "));
158                        }
159                }
160                return message.toString();
161        }
162
163        private String indentStackTrace(Exception ex, String indent) {
164                return indentLines(printStackTrace(ex), indent);
165        }
166
167        private String printStackTrace(Exception ex) {
168                StringWriter stackTrace = new StringWriter();
169                PrintWriter printer = new PrintWriter(stackTrace);
170                ex.printStackTrace(printer);
171                return stackTrace.toString();
172        }
173
174        private String indentLines(String input, String indent) {
175                StringWriter indented = new StringWriter();
176                PrintWriter writer = new PrintWriter(indented);
177                BufferedReader reader = new BufferedReader(new StringReader(input));
178                reader.lines().forEach((line) -> writer.println(indent + line));
179                return indented.toString();
180        }
181
182        private DefaultProfileSelector createProfileSelector() {
183                DefaultProfileSelector selector = new DefaultProfileSelector();
184
185                selector.addProfileActivator(new FileProfileActivator()
186                                .setPathTranslator(new DefaultPathTranslator()));
187                selector.addProfileActivator(new JdkVersionProfileActivator());
188                selector.addProfileActivator(new PropertyProfileActivator());
189                selector.addProfileActivator(new OperatingSystemProfileActivator());
190                return selector;
191        }
192
193        private List<org.apache.maven.model.Profile> createModelProfiles(
194                        List<Profile> profiles) {
195                List<org.apache.maven.model.Profile> modelProfiles = new ArrayList<>();
196                for (Profile profile : profiles) {
197                        org.apache.maven.model.Profile modelProfile = new org.apache.maven.model.Profile();
198                        modelProfile.setId(profile.getId());
199                        if (profile.getActivation() != null) {
200                                modelProfile
201                                                .setActivation(createModelActivation(profile.getActivation()));
202                        }
203                        modelProfiles.add(modelProfile);
204                }
205                return modelProfiles;
206        }
207
208        private org.apache.maven.model.Activation createModelActivation(
209                        Activation activation) {
210                org.apache.maven.model.Activation modelActivation = new org.apache.maven.model.Activation();
211                modelActivation.setActiveByDefault(activation.isActiveByDefault());
212                if (activation.getFile() != null) {
213                        ActivationFile activationFile = new ActivationFile();
214                        activationFile.setExists(activation.getFile().getExists());
215                        activationFile.setMissing(activation.getFile().getMissing());
216                        modelActivation.setFile(activationFile);
217                }
218                modelActivation.setJdk(activation.getJdk());
219                if (activation.getOs() != null) {
220                        ActivationOS os = new ActivationOS();
221                        os.setArch(activation.getOs().getArch());
222                        os.setFamily(activation.getOs().getFamily());
223                        os.setName(activation.getOs().getName());
224                        os.setVersion(activation.getOs().getVersion());
225                        modelActivation.setOs(os);
226                }
227                if (activation.getProperty() != null) {
228                        ActivationProperty property = new ActivationProperty();
229                        property.setName(activation.getProperty().getName());
230                        property.setValue(activation.getProperty().getValue());
231                        modelActivation.setProperty(property);
232                }
233                return modelActivation;
234        }
235
236        public boolean getOffline() {
237                return this.offline;
238        }
239
240        public MirrorSelector getMirrorSelector() {
241                return this.mirrorSelector;
242        }
243
244        public AuthenticationSelector getAuthenticationSelector() {
245                return this.authenticationSelector;
246        }
247
248        public ProxySelector getProxySelector() {
249                return this.proxySelector;
250        }
251
252        public String getLocalRepository() {
253                return this.localRepository;
254        }
255
256        public List<Profile> getActiveProfiles() {
257                return this.activeProfiles;
258        }
259
260        private static final class SpringBootCliProfileActivationContext
261                        implements ProfileActivationContext {
262
263                private final List<String> activeProfiles;
264
265                SpringBootCliProfileActivationContext(List<String> activeProfiles) {
266                        this.activeProfiles = activeProfiles;
267                }
268
269                @Override
270                public List<String> getActiveProfileIds() {
271                        return this.activeProfiles;
272                }
273
274                @Override
275                public List<String> getInactiveProfileIds() {
276                        return Collections.emptyList();
277                }
278
279                @SuppressWarnings({ "unchecked", "rawtypes" })
280                @Override
281                public Map<String, String> getSystemProperties() {
282                        return (Map) System.getProperties();
283                }
284
285                @Override
286                public Map<String, String> getUserProperties() {
287                        return Collections.emptyMap();
288                }
289
290                @Override
291                public File getProjectDirectory() {
292                        return new File(".");
293                }
294
295                @Override
296                public Map<String, String> getProjectProperties() {
297                        return Collections.emptyMap();
298                }
299
300        }
301
302        private static final class SpringBootCliModelProblemCollector
303                        implements ModelProblemCollector {
304
305                private final List<ModelProblemCollectorRequest> problems = new ArrayList<>();
306
307                @Override
308                public void add(ModelProblemCollectorRequest req) {
309                        this.problems.add(req);
310                }
311
312                List<ModelProblemCollectorRequest> getProblems() {
313                        return this.problems;
314                }
315
316        }
317
318}