/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package net.acesinc.convergentui.content;
import com.netflix.zuul.constants.ZuulHeaders;
import com.netflix.zuul.context.RequestContext;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.GZIPInputStream;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
/**
* The Content Service is responsible for getting all the content.
* It uses a Caching Manager to cache the content from the downstream servers if
* possible. It also leverages our ContentFetcherCommand which is a
* Hystrix Circuit Breaker enabled content fetcher. This fetcher is what goes to
* the actual live service to get the content either on the first try, or when the
* cache is empty for the specified cacheName.
*
* @author andrewserff
*/
@Service
public class ContentService {
private static final Logger log = LoggerFactory.getLogger(ContentService.class);
@Autowired
private RestTemplate restTemplate;
@PostConstruct
public void setup() {
//We have to add some extra special handlers.
restTemplate.getMessageConverters().add(new BufferedImageHttpMessageConverter());
restTemplate.getMessageConverters().add(new TextHttpMessageConverter());
}
/**
* This is just a pass through/helper method for the full getContentFromService.
*
* @param location The remote location of the content
* @param cacheName The unique cache name for this piece of content
* @param context THe RequestContext for this request
* @return The content returned from the service as a string.
*/
public ContentResponse getContentFromService(String location, String cacheName, RequestContext context) {
return getContentFromService(location, cacheName, true, context);
}
/**
* This method will attempt to get the content from the named location.
* Important things to know about the location: - The location should
* contain a service name as the host. This service name will be resolved in
* the Eureka Discovery Service. - We use caching. We will first attempt to
* get the content out of the cache and it if is not there, we will ask the
* live service for the content. - You can disable caching by passing false
* for the useCache - When we get live content, we use a Hystrix Circuit
* Breaker. If the request fails, we return an empty response.
*
* @param location The remote location of the content
* @param cacheName The unique cache name for this piece of content
* @param useCache Use caching if true, skip otherwise.
* @param context THe RequestContext for this request
* @return The content returned from the service as a string.
*/
@Cacheable(value = "service-content", key = "#cacheName", unless = "!#useCache or #result.error")
public ContentResponse getContentFromService(String location, String cacheName, boolean useCache, RequestContext context) {
ContentFetchCommand fetcher = new ContentFetchCommand(location, context, restTemplate);
return fetcher.execute();
}
public String getDownstreamResponse() {
RequestContext context = RequestContext.getCurrentContext();
// there is no body to send
if (context.getResponseBody() == null && context.getResponseDataStream() == null) {
return null;
}
String body = null;
if (RequestContext.getCurrentContext().getResponseBody() != null) {
body = RequestContext.getCurrentContext().getResponseBody();
} else {
try {
HttpServletResponse servletResponse = context.getResponse();
servletResponse.setCharacterEncoding("UTF-8");
InputStream is = null;
boolean isGzipRequested = false;
final String requestEncoding = context.getRequest().getHeader(
ZuulHeaders.ACCEPT_ENCODING);
if (requestEncoding != null && requestEncoding.equals("gzip")) {
isGzipRequested = true;
}
is = context.getResponseDataStream();
InputStream inputStream = is;
if (is != null) {
if (context.sendZuulResponse()) {
// if origin response is gzipped, and client has not requested gzip,
// decompress stream
// before sending to client
// else, stream gzip directly to client
if (context.getResponseGZipped() && !isGzipRequested) {
try {
inputStream = new GZIPInputStream(is);
} catch (java.util.zip.ZipException ex) {
System.out.println("gzip expected but not "
+ "received assuming unencoded response"
+ RequestContext.getCurrentContext().getRequest()
.getRequestURL().toString());
inputStream = is;
}
} else if (context.getResponseGZipped() && isGzipRequested) {
servletResponse.setHeader(ZuulHeaders.CONTENT_ENCODING, "gzip");
}
body = IOUtils.toString(inputStream, "utf8");
}
}
} catch (IOException ioe) {
log.warn("IOException getting output stream", ioe);
}
}
return body;
}
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}