/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.cli.compiler.maven;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.maven.model.ActivationFile;
import org.apache.maven.model.ActivationOS;
import org.apache.maven.model.ActivationProperty;
import org.apache.maven.model.building.ModelProblemCollector;
import org.apache.maven.model.building.ModelProblemCollectorRequest;
import org.apache.maven.model.path.DefaultPathTranslator;
import org.apache.maven.model.profile.DefaultProfileSelector;
import org.apache.maven.model.profile.ProfileActivationContext;
import org.apache.maven.model.profile.activation.FileProfileActivator;
import org.apache.maven.model.profile.activation.JdkVersionProfileActivator;
import org.apache.maven.model.profile.activation.OperatingSystemProfileActivator;
import org.apache.maven.model.profile.activation.PropertyProfileActivator;
import org.apache.maven.settings.Activation;
import org.apache.maven.settings.Mirror;
import org.apache.maven.settings.Profile;
import org.apache.maven.settings.Proxy;
import org.apache.maven.settings.Server;
import org.apache.maven.settings.Settings;
import org.apache.maven.settings.crypto.SettingsDecryptionResult;
import org.eclipse.aether.repository.Authentication;
import org.eclipse.aether.repository.AuthenticationSelector;
import org.eclipse.aether.repository.MirrorSelector;
import org.eclipse.aether.repository.ProxySelector;
import org.eclipse.aether.util.repository.AuthenticationBuilder;
import org.eclipse.aether.util.repository.ConservativeAuthenticationSelector;
import org.eclipse.aether.util.repository.DefaultAuthenticationSelector;
import org.eclipse.aether.util.repository.DefaultMirrorSelector;
import org.eclipse.aether.util.repository.DefaultProxySelector;
/**
* An encapsulation of settings read from a user's Maven settings.xml.
*
* @author Andy Wilkinson
* @since 1.3.0
* @see MavenSettingsReader
*/
public class MavenSettings {
private final boolean offline;
private final MirrorSelector mirrorSelector;
private final AuthenticationSelector authenticationSelector;
private final ProxySelector proxySelector;
private final String localRepository;
private final List<Profile> activeProfiles;
/**
* Create a new {@link MavenSettings} instance.
* @param settings the source settings
* @param decryptedSettings the decrypted settings
*/
public MavenSettings(Settings settings, SettingsDecryptionResult decryptedSettings) {
this.offline = settings.isOffline();
this.mirrorSelector = createMirrorSelector(settings);
this.authenticationSelector = createAuthenticationSelector(decryptedSettings);
this.proxySelector = createProxySelector(decryptedSettings);
this.localRepository = settings.getLocalRepository();
this.activeProfiles = determineActiveProfiles(settings);
}
private MirrorSelector createMirrorSelector(Settings settings) {
DefaultMirrorSelector selector = new DefaultMirrorSelector();
for (Mirror mirror : settings.getMirrors()) {
selector.add(mirror.getId(), mirror.getUrl(), mirror.getLayout(), false,
mirror.getMirrorOf(), mirror.getMirrorOfLayouts());
}
return selector;
}
private AuthenticationSelector createAuthenticationSelector(
SettingsDecryptionResult decryptedSettings) {
DefaultAuthenticationSelector selector = new DefaultAuthenticationSelector();
for (Server server : decryptedSettings.getServers()) {
AuthenticationBuilder auth = new AuthenticationBuilder();
auth.addUsername(server.getUsername()).addPassword(server.getPassword());
auth.addPrivateKey(server.getPrivateKey(), server.getPassphrase());
selector.add(server.getId(), auth.build());
}
return new ConservativeAuthenticationSelector(selector);
}
private ProxySelector createProxySelector(
SettingsDecryptionResult decryptedSettings) {
DefaultProxySelector selector = new DefaultProxySelector();
for (Proxy proxy : decryptedSettings.getProxies()) {
Authentication authentication = new AuthenticationBuilder()
.addUsername(proxy.getUsername()).addPassword(proxy.getPassword())
.build();
selector.add(
new org.eclipse.aether.repository.Proxy(proxy.getProtocol(),
proxy.getHost(), proxy.getPort(), authentication),
proxy.getNonProxyHosts());
}
return selector;
}
private List<Profile> determineActiveProfiles(Settings settings) {
SpringBootCliModelProblemCollector problemCollector = new SpringBootCliModelProblemCollector();
List<org.apache.maven.model.Profile> activeModelProfiles = createProfileSelector()
.getActiveProfiles(createModelProfiles(settings.getProfiles()),
new SpringBootCliProfileActivationContext(
settings.getActiveProfiles()),
problemCollector);
if (!problemCollector.getProblems().isEmpty()) {
throw new IllegalStateException(createFailureMessage(problemCollector));
}
List<Profile> activeProfiles = new ArrayList<>();
Map<String, Profile> profiles = settings.getProfilesAsMap();
for (org.apache.maven.model.Profile modelProfile : activeModelProfiles) {
activeProfiles.add(profiles.get(modelProfile.getId()));
}
return activeProfiles;
}
private String createFailureMessage(
SpringBootCliModelProblemCollector problemCollector) {
StringWriter message = new StringWriter();
PrintWriter printer = new PrintWriter(message);
printer.println("Failed to determine active profiles:");
for (ModelProblemCollectorRequest problem : problemCollector.getProblems()) {
printer.println(" " + problem.getMessage() + (problem.getLocation() != null
? " at " + problem.getLocation() : ""));
if (problem.getException() != null) {
printer.println(indentStackTrace(problem.getException(), " "));
}
}
return message.toString();
}
private String indentStackTrace(Exception ex, String indent) {
return indentLines(printStackTrace(ex), indent);
}
private String printStackTrace(Exception ex) {
StringWriter stackTrace = new StringWriter();
PrintWriter printer = new PrintWriter(stackTrace);
ex.printStackTrace(printer);
return stackTrace.toString();
}
private String indentLines(String input, String indent) {
StringWriter indented = new StringWriter();
PrintWriter writer = new PrintWriter(indented);
String line;
BufferedReader reader = new BufferedReader(new StringReader(input));
try {
while ((line = reader.readLine()) != null) {
writer.println(indent + line);
}
}
catch (IOException ex) {
return input;
}
return indented.toString();
}
private DefaultProfileSelector createProfileSelector() {
DefaultProfileSelector selector = new DefaultProfileSelector();
selector.addProfileActivator(new FileProfileActivator()
.setPathTranslator(new DefaultPathTranslator()));
selector.addProfileActivator(new JdkVersionProfileActivator());
selector.addProfileActivator(new PropertyProfileActivator());
selector.addProfileActivator(new OperatingSystemProfileActivator());
return selector;
}
private List<org.apache.maven.model.Profile> createModelProfiles(
List<Profile> profiles) {
List<org.apache.maven.model.Profile> modelProfiles = new ArrayList<>();
for (Profile profile : profiles) {
org.apache.maven.model.Profile modelProfile = new org.apache.maven.model.Profile();
modelProfile.setId(profile.getId());
if (profile.getActivation() != null) {
modelProfile
.setActivation(createModelActivation(profile.getActivation()));
}
modelProfiles.add(modelProfile);
}
return modelProfiles;
}
private org.apache.maven.model.Activation createModelActivation(
Activation activation) {
org.apache.maven.model.Activation modelActivation = new org.apache.maven.model.Activation();
modelActivation.setActiveByDefault(activation.isActiveByDefault());
if (activation.getFile() != null) {
ActivationFile activationFile = new ActivationFile();
activationFile.setExists(activation.getFile().getExists());
activationFile.setMissing(activation.getFile().getMissing());
modelActivation.setFile(activationFile);
}
modelActivation.setJdk(activation.getJdk());
if (activation.getOs() != null) {
ActivationOS os = new ActivationOS();
os.setArch(activation.getOs().getArch());
os.setFamily(activation.getOs().getFamily());
os.setName(activation.getOs().getName());
os.setVersion(activation.getOs().getVersion());
modelActivation.setOs(os);
}
if (activation.getProperty() != null) {
ActivationProperty property = new ActivationProperty();
property.setName(activation.getProperty().getName());
property.setValue(activation.getProperty().getValue());
modelActivation.setProperty(property);
}
return modelActivation;
}
public boolean getOffline() {
return this.offline;
}
public MirrorSelector getMirrorSelector() {
return this.mirrorSelector;
}
public AuthenticationSelector getAuthenticationSelector() {
return this.authenticationSelector;
}
public ProxySelector getProxySelector() {
return this.proxySelector;
}
public String getLocalRepository() {
return this.localRepository;
}
public List<Profile> getActiveProfiles() {
return this.activeProfiles;
}
private static final class SpringBootCliProfileActivationContext
implements ProfileActivationContext {
private final List<String> activeProfiles;
SpringBootCliProfileActivationContext(List<String> activeProfiles) {
this.activeProfiles = activeProfiles;
}
@Override
public List<String> getActiveProfileIds() {
return this.activeProfiles;
}
@Override
public List<String> getInactiveProfileIds() {
return Collections.emptyList();
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public Map<String, String> getSystemProperties() {
return (Map) System.getProperties();
}
@Override
public Map<String, String> getUserProperties() {
return Collections.emptyMap();
}
@Override
public File getProjectDirectory() {
return new File(".");
}
@Override
public Map<String, String> getProjectProperties() {
return Collections.emptyMap();
}
}
private static final class SpringBootCliModelProblemCollector
implements ModelProblemCollector {
private final List<ModelProblemCollectorRequest> problems = new ArrayList<>();
@Override
public void add(ModelProblemCollectorRequest req) {
this.problems.add(req);
}
List<ModelProblemCollectorRequest> getProblems() {
return this.problems;
}
}
}