/*
* Copyright 2014 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.xd.shell.command;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;
import java.util.TimeZone;
import java.util.TreeMap;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.shell.CommandLine;
import org.springframework.shell.core.CommandMarker;
import org.springframework.shell.core.annotation.CliCommand;
import org.springframework.shell.core.annotation.CliOption;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.xd.rest.client.impl.SpringXDTemplate;
import org.springframework.xd.shell.Configuration;
import org.springframework.xd.shell.Target;
import org.springframework.xd.shell.Target.TargetStatus;
import org.springframework.xd.shell.XDShell;
import org.springframework.xd.shell.util.CommonUtils;
import org.springframework.xd.shell.util.UiUtils;
/**
* Defines several shell commands in order to configure the shell itself. The
* commands will set properties on the {@link Configuration} object.
*
* @author Gunnar Hillert
* @author Marius Bogoevici
* @since 1.0
*
*/
@Component
public class ConfigCommands implements CommandMarker, InitializingBean {
private static final Log logger = LogFactory.getLog(XDShell.class);
@Autowired
private CommandLine commandLine;
@Autowired
private Configuration configuration;
@Autowired
private XDShell xdShell;
@Autowired
private UserInput userInput;
public ConfigCommands() {
}
@CliCommand(value = {"admin config server"}, help = "Configure the XD admin server to use")
public String target(
@CliOption(mandatory = false, key = {"", "uri"},
help = "the location of the XD Admin REST endpoint",
unspecifiedDefaultValue = Target.DEFAULT_TARGET) String targetUriString,
@CliOption(mandatory = false, key = {"username"},
help = "the username for authenticated access to the Admin REST endpoint",
unspecifiedDefaultValue = Target.DEFAULT_USERNAME) String targetUsername,
@CliOption(mandatory = false, key = {"password"},
help = "the password for authenticated access to the Admin REST endpoint (valid only with a username)",
specifiedDefaultValue = Target.DEFAULT_SPECIFIED_PASSWORD,
unspecifiedDefaultValue = Target.DEFAULT_UNSPECIFIED_PASSWORD) String targetPassword) {
try {
if (!StringUtils.isEmpty(targetPassword) && StringUtils.isEmpty(targetUsername)) {
return "A password may be specified only together with a username";
}
if (Target.DEFAULT_SPECIFIED_PASSWORD.equalsIgnoreCase(targetPassword) && !StringUtils.isEmpty(targetUsername)) {
// read password from the command line
targetPassword = userInput.prompt("Password", "", false);
}
configuration.setTarget(new Target(targetUriString, targetUsername, targetPassword));
if (configuration.getTarget().getTargetCredentials() != null) {
BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials(
configuration.getTarget().getTargetCredentials().getUsername(),
configuration.getTarget().getTargetCredentials().getPassword()));
CloseableHttpClient httpClient = HttpClientBuilder.create().setDefaultCredentialsProvider(credentialsProvider).build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
this.xdShell.setSpringXDOperations(new SpringXDTemplate(requestFactory, configuration.getTarget().getTargetUri()));
}
else {
this.xdShell.setSpringXDOperations(new SpringXDTemplate(configuration.getTarget().getTargetUri()));
}
configuration.getTarget().setTargetResultMessage(
String.format("Successfully targeted %s", configuration.getTarget().getTargetUri()));
}
catch (Exception e) {
configuration.getTarget().setTargetException(e);
this.xdShell.setSpringXDOperations(null);
configuration.getTarget().setTargetResultMessage(
String.format("Unable to contact XD Admin Server at '%s'.",
targetUriString));
if (logger.isTraceEnabled()) {
logger.trace(configuration.getTarget().getTargetResultMessage(), e);
}
}
return configuration.getTarget().getTargetResultMessage();
}
@CliCommand(value = {"admin config info"}, help = "Show the XD admin server being used")
public String info() {
final Map<String, String> statusValues = new TreeMap<String, String>();
final Target target = configuration.getTarget();
statusValues.put("Target", target.getTargetUriAsString());
if (target.getTargetCredentials() != null) {
statusValues.put("Credentials", target.getTargetCredentials().getDisplayableContents());
}
statusValues.put("Result", target.getTargetResultMessage() != null ? target.getTargetResultMessage() : "");
statusValues.put("Timezone used", CommonUtils.getTimeZoneNameWithOffset(this.configuration.getClientTimeZone()));
final StringBuilder sb = new StringBuilder(UiUtils.renderParameterInfoDataAsTable(statusValues, false, 66));
if (TargetStatus.ERROR.equals(target.getStatus())) {
sb.append(UiUtils.HORIZONTAL_LINE);
sb.append("An exception ocurred during targeting:\n");
final StringWriter stringWriter = new StringWriter();
target.getTargetException().printStackTrace(new PrintWriter(stringWriter));
sb.append(stringWriter.toString());
}
return sb.toString();
}
/**
* Retrieve a list of available {@link TimeZone} Ids via a Spring XD Shell command.
*/
@CliCommand(value = "admin config timezone list", help = "List all timezones")
public String listTimeZones() {
final StringBuilder timeZones = new StringBuilder();
for (String timeZone : TimeZone.getAvailableIDs()) {
timeZones.append(timeZone + "\n");
}
return timeZones.toString();
}
/**
* Allows for setting the {@link TimeZone} via a Spring XD Shell command.
*/
@CliCommand(value = "admin config timezone set", help = "Set the timezone of the Spring XD Shell (Not persisted)")
public String setTimeZone(
@CliOption(mandatory = true, key = {"", "timeZone"}, help = "the id of the timezone, "
+ "You can obtain a list of timezone ids using 'admin config timezone list', "
+ "If an invalid timezone id is provided, then 'Greenwich Mean Time' "
+ "is being used") String timeZoneId) {
final TimeZone newCientTimeZone = TimeZone.getTimeZone(timeZoneId);
this.configuration.setClientTimeZone(newCientTimeZone);
return "TimeZone set to " + newCientTimeZone.getDisplayName();
}
/**
* Initialize the default {@link Target} for the XD Admin Server. It will use
* the constants {@link Target#DEFAULT_HOST}, {@link Target#DEFAULT_PORT}
* and {@link Target#DEFAULT_SCHEME}.
*
* Alternatively, the host and port can also be set using the {@code --host}
* and {@code --port} command line parameters.
*/
@Override
public void afterPropertiesSet() throws Exception {
target(getDefaultUri().toString(), getDefaultUsername(), getDefaultPassword());
}
private URI getDefaultUri() throws URISyntaxException {
int port = Target.DEFAULT_PORT;
String host = Target.DEFAULT_HOST;
if (commandLine.getArgs() != null) {
String[] args = commandLine.getArgs();
int i = 0;
while (i < args.length) {
String arg = args[i++];
if (arg.equals("--host")) {
host = args[i++];
}
else if (arg.equals("--port")) {
port = Integer.valueOf(args[i++]);
}
else {
i--;
break;
}
}
}
return new URI(Target.DEFAULT_SCHEME, null, host, port, null, null, null);
}
private String getDefaultUsername() {
int indexOfUserParameter = ArrayUtils.indexOf(commandLine.getArgs(), "--username");
// if '--username' exists and it is not the last in the list of arguments, the next argument is the password
if (indexOfUserParameter >= 0 && indexOfUserParameter < commandLine.getArgs().length - 1) {
return commandLine.getArgs()[indexOfUserParameter + 1];
}
else {
return Target.DEFAULT_USERNAME;
}
}
private String getDefaultPassword() {
int indexOfPasswordParameter = ArrayUtils.indexOf(commandLine.getArgs(), "--password");
// if '--password' exists and it is not the last in the list of arguments, the next argument is the password
if (indexOfPasswordParameter >= 0) {
if (indexOfPasswordParameter < commandLine.getArgs().length - 1) {
return commandLine.getArgs()[indexOfPasswordParameter + 1];
}
else {
return Target.DEFAULT_SPECIFIED_PASSWORD;
}
}
else {
return Target.DEFAULT_SPECIFIED_PASSWORD;
}
}
}