/*
* Copyright 2013 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.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.HashMap;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.shell.core.CommandMarker;
import org.springframework.shell.core.annotation.CliCommand;
import org.springframework.shell.core.annotation.CliOption;
import org.springframework.shell.support.util.OsUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
/**
* Http commands.
*
* @author Jon Brisbin
* @author Ilayaperumal Gopinathan
* @author Gunnar Hillert
* @author Eric Bottard
* @author David Turanski
*/
@Component
public class HttpCommands implements CommandMarker {
private static final String DEFAULT_MEDIA_TYPE = MediaType.TEXT_PLAIN_VALUE + "; Charset=UTF-8";
private static final String POST_HTTPSOURCE = "http post";
private static final String GET_HTTPSOURCE = "http get";
@CliCommand(value = { POST_HTTPSOURCE }, help = "POST data to http endpoint")
public String postHttp(
@CliOption(mandatory = false, key = { "", "target" }, help = "the location to post to", unspecifiedDefaultValue = "http://localhost:9000") String target,
@CliOption(mandatory = false, key = "data", help = "the text payload to post. exclusive with file. embedded double quotes are not supported if next to a space character") String data,
@CliOption(mandatory = false, key = "file", help = "filename to read data from. exclusive with data") File file,
@CliOption(mandatory = false, key = "contentType", help = "the content-type to use. file is also read using the specified charset", unspecifiedDefaultValue = DEFAULT_MEDIA_TYPE) MediaType mediaType)
throws IOException {
Assert.isTrue(file != null || data != null, "One of 'file' or 'data' must be set");
Assert.isTrue(file == null || data == null, "Only one of 'file' or 'data' must be set");
if (mediaType.getCharSet() == null) {
mediaType = new MediaType(mediaType, Collections.singletonMap("charset",
Charset.defaultCharset().toString()));
}
if (file != null) {
InputStreamReader isr = new InputStreamReader(new FileInputStream(file), mediaType.getCharSet());
data = FileCopyUtils.copyToString(isr);
}
final StringBuilder buffer = new StringBuilder();
URI requestURI = URI.create(target);
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(mediaType);
final HttpEntity<String> request = new HttpEntity<String>(data, headers);
try {
outputRequest("POST", requestURI, mediaType, data, buffer);
ResponseEntity<String> response = createRestTemplate(buffer).postForEntity(requestURI, request, String.class);
outputResponse(response, buffer);
if (!response.getStatusCode().equals(HttpStatus.OK)) {
buffer.append(OsUtils.LINE_SEPARATOR).append(
String.format("Error sending data '%s' to '%s'", data, target));
}
return buffer.toString();
}
catch (ResourceAccessException e) {
return String.format(buffer.toString() + "Failed to access http endpoint %s", target);
}
catch (Exception e) {
return String.format(buffer.toString() + "Failed to send data to http endpoint %s", target);
}
}
@CliCommand(value = { GET_HTTPSOURCE }, help = "Make GET request to http endpoint")
public String getHttp(
@CliOption(mandatory = false, key = { "", "target" }, help = "the URL to make the request to", unspecifiedDefaultValue = "http://localhost:9393") String target)
throws IOException {
final StringBuilder buffer = new StringBuilder();
URI requestURI = URI.create(target);
try {
outputRequest("GET", requestURI, null, "", buffer);
ResponseEntity<String> response = createRestTemplate(buffer).getForEntity(requestURI, String.class);
outputResponse(response, buffer);
if (!response.getStatusCode().equals(HttpStatus.OK)) {
buffer.append(OsUtils.LINE_SEPARATOR).append(
String.format("Error sending request to '%s'", target));
}
return buffer.toString();
}
catch (ResourceAccessException e) {
return String.format(buffer.toString() + "Failed to access http endpoint %s", target);
}
catch (Exception e) {
return String.format(buffer.toString() + "Failed to get data from http endpoint %s", target);
}
}
private RestTemplate createRestTemplate(final StringBuilder buffer) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new ResponseErrorHandler() {
@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
HttpStatus status = response.getStatusCode();
return (status == HttpStatus.BAD_GATEWAY || status == HttpStatus.GATEWAY_TIMEOUT || status == HttpStatus.INTERNAL_SERVER_ERROR);
}
@Override
public void handleError(ClientHttpResponse response) throws IOException {
outputError(response.getStatusCode(), buffer);
}
});
return restTemplate;
}
private void outputRequest(String method, URI requestUri, MediaType mediaType, String requestData,
StringBuilder buffer) {
buffer.append("> ").append(method).append(' ');
if (mediaType != null) {
buffer.append("(").append(mediaType.toString()).append(") ");
}
buffer.append(requestUri.toString()).append(" ").append(requestData).append(OsUtils.LINE_SEPARATOR);
}
private void outputResponse(ResponseEntity<String> response, StringBuilder buffer) {
buffer.append("> ").append(response.getStatusCode().value()).append(" ").append(response.getStatusCode().name()).append(
OsUtils.LINE_SEPARATOR);
String maybeJson = response.getBody();
if (maybeJson != null) {
buffer.append(prettyPrintIfJson(maybeJson));
}
}
private String prettyPrintIfJson(String maybeJson) {
JsonFactory factory = new JsonFactory();
ObjectMapper mapper = new ObjectMapper(factory);
TypeReference<HashMap<String,Object>> typeRef = new TypeReference<HashMap<String,Object>>() {};
try {
return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(mapper.readValue(maybeJson, typeRef));
}
catch (IOException e) {
// Not JSON? Return unchanged
return maybeJson;
}
}
private void outputError(HttpStatus status, StringBuilder buffer) {
buffer.append("> ").append(status.value()).append(" ").append(status.name()).append(OsUtils.LINE_SEPARATOR);
}
}