/* * 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(); } }