HttpServletResponseMapper.java

/*
 * #%L
 * wcm.io
 * %%
 * Copyright (C) 2015 wcm.io
 * %%
 * 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.
 * #L%
 */
package io.wcm.caravan.io.http.impl.servletclient;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;

import com.google.common.base.Charsets;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;

import io.wcm.caravan.io.http.response.CaravanHttpResponse;
import io.wcm.caravan.io.http.response.CaravanHttpResponseBuilder;

/**
 * Mapper from {@link CaravanHttpResponse} to {@link HttpServletResponse}.
 */
public class HttpServletResponseMapper implements HttpServletResponse {

  private String characterEncoding = Charsets.UTF_8.toString();
  private String contentType;
  private ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
  private int bufferSize = 4096;
  private Locale locale;
  private final List<Cookie> cookies = Lists.newArrayList();
  private final Multimap<String, String> headers = HashMultimap.create();
  private int status = HttpServletResponse.SC_OK;
  private String reason = "OK";

  @Override
  public String getCharacterEncoding() {
    return characterEncoding;
  }

  @Override
  public String getContentType() {
    return contentType;
  }

  @Override
  public ServletOutputStream getOutputStream() throws IOException {

    return new ServletOutputStream() {

      @Override
      public void write(int b) throws IOException {
        outputStream.write(b);
      }
    };
  }

  @Override
  public PrintWriter getWriter() throws IOException {
    return new PrintWriter(outputStream);
  }

  @Override
  public void setCharacterEncoding(String charset) {
    characterEncoding = charset;
  }

  @Override
  public void setContentLength(int len) {
    setIntHeader("Content-Length", len);

    // if the content length is known in advance then resize the ByteArrayOutputSteram accordingly
    // (but only if nothing has yet been written to the output stream)
    if (len > 0 && outputStream.size() == 0) {
      outputStream = new ByteArrayOutputStream(len);
    }
  }

  @Override
  public void setContentType(String type) {
    contentType = type;
  }

  @Override
  public void setBufferSize(int size) {
    bufferSize = size;
  }

  @Override
  public int getBufferSize() {
    return bufferSize;
  }

  @Override
  public void flushBuffer() throws IOException {
    outputStream.flush();
  }

  @Override
  public void resetBuffer() {
    outputStream.reset();
  }

  @Override
  public boolean isCommitted() {
    return false;
  }

  @Override
  public void reset() {
    resetBuffer();
    this.characterEncoding = null;
    this.contentType = null;
    this.locale = null;
    this.cookies.clear();
    this.headers.clear();
    this.status = HttpServletResponse.SC_OK;
    this.reason = null;
  }

  @Override
  public void setLocale(Locale loc) {
    locale = loc;
  }

  @Override
  public Locale getLocale() {
    return locale;
  }

  @Override
  public void addCookie(Cookie cookie) {
    cookies.add(cookie);
  }

  @Override
  public boolean containsHeader(String name) {
    return headers.containsKey(name);
  }

  @Override
  public String encodeURL(String url) {
    return url;
  }

  @Override
  public String encodeRedirectURL(String url) {
    return encodeURL(url);
  }

  @Override
  public String encodeUrl(String url) {
    return encodeURL(url);
  }

  @Override
  public String encodeRedirectUrl(String url) {
    return encodeRedirectURL(url);
  }

  @Override
  public void sendError(int sc, String msg) throws IOException {
    status = sc;
    reason = msg;
  }

  @Override
  public void sendError(int sc) throws IOException {
    status = sc;
  }

  @Override
  public void sendRedirect(String location) throws IOException {
    setHeader("Location", location);
    setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
  }

  @Override
  public void setDateHeader(String name, long date) {
    String value = formatDate(date);
    setHeader(name, value);
  }

  private String formatDate(long date) {
    SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
    dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
    return dateFormat.format(new Date(date));
  }

  @Override
  public void addDateHeader(String name, long date) {
    String value = formatDate(date);
    addHeader(name, value);
  }

  @Override
  public void setHeader(String name, String value) {
    headers.replaceValues(name, Collections.singletonList(value));
  }

  @Override
  public void addHeader(String name, String value) {
    headers.put(name, value);
  }

  @Override
  public void setIntHeader(String name, int value) {
    setHeader(name, String.valueOf(value));
  }

  @Override
  public void addIntHeader(String name, int value) {
    addHeader(name, String.valueOf(value));
  }

  @Override
  public void setStatus(int sc) {
    status = sc;
  }

  @Override
  public void setStatus(int sc, String sm) {
    setStatus(sc);
    reason = sm;
  }

  @Override
  public int getStatus() {
    return status;
  }

  @Override
  public String getHeader(String name) {
    Collection<String> values = headers.get(name);
    return values.isEmpty() ? null : values.iterator().next();
  }

  @Override
  public Collection<String> getHeaders(String name) {
    return headers.get(name);
  }

  @Override
  public Collection<String> getHeaderNames() {
    return headers.keySet();
  }

  public CaravanHttpResponse getResponse() {
    return new CaravanHttpResponseBuilder()
    .body(outputStream.toByteArray())
    .headers(headers)
    .reason(reason)
    .status(status)
    .build();
  }

}