/* * Copyright © 2016 Cask Data, Inc. * * 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 co.cask.cdap.internal.app.runtime.service.http; import co.cask.cdap.api.service.http.HttpContentProducer; import co.cask.cdap.api.service.http.HttpServiceResponder; import com.google.common.base.Charsets; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Multimap; import com.google.gson.Gson; import org.apache.twill.filesystem.Location; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.handler.codec.http.HttpResponseStatus; import java.io.IOException; import java.lang.reflect.Type; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.Map; import javax.annotation.Nullable; /** * An abstract implementation of {@link HttpServiceResponder} to simplify * concrete implementation of {@link HttpServiceResponder}. */ public abstract class AbstractHttpServiceResponder implements HttpServiceResponder { @Override public final void sendJson(Object object) { sendJson(HttpResponseStatus.OK.getCode(), object); } @Override public final void sendJson(int status, Object object) { sendJson(status, object, object.getClass(), new Gson()); } @Override public final void sendJson(int status, Object object, Type type, Gson gson) { doSend(status, "application/json", ChannelBuffers.wrappedBuffer(Charsets.UTF_8.encode(gson.toJson(object, type))), null, null); } @Override public final void sendString(String data) { sendString(HttpResponseStatus.OK.getCode(), data, Charsets.UTF_8); } @Override public final void sendString(int status, String data, Charset charset) { doSend(status, "text/plain; charset=" + charset.name(), ChannelBuffers.wrappedBuffer(charset.encode(data)), null, null); } @Override public final void sendStatus(int status) { sendStatus(status, ImmutableMap.<String, String>of()); } @Override public final void sendStatus(int status, Map<String, String> headers) { sendStatus(status, headers.entrySet()); } @Override public final void sendStatus(int status, Iterable<? extends Map.Entry<String, String>> headers) { doSend(status, "text/plain", null, null, createMultimap(headers)); } @Override public final void sendError(int status, String errorMessage) { sendString(status, errorMessage, Charsets.UTF_8); } @Override public final void send(int status, ByteBuffer content, String contentType, Map<String, String> headers) { send(status, content, contentType, headers.entrySet()); } @Override public final void send(int status, ByteBuffer content, String contentType, Iterable<? extends Map.Entry<String, String>> headers) { doSend(status, contentType, ChannelBuffers.copiedBuffer(content), null, createMultimap(headers)); } @Override public final void send(int status, Location location, String contentType) throws IOException { send(status, location, contentType, ImmutableMap.<String, String>of()); } @Override public final void send(int status, Location location, String contentType, Map<String, String> headers) throws IOException { send(status, location, contentType, headers.entrySet()); } @Override public final void send(int status, Location location, String contentType, Iterable<? extends Map.Entry<String, String>> headers) throws IOException { send(status, new LocationHttpContentProducer(location), contentType, headers); } @Override public final void send(int status, HttpContentProducer producer, String contentType) { send(status, producer, contentType, ImmutableMap.<String, String>of()); } @Override public final void send(int status, HttpContentProducer producer, String contentType, Map<String, String> headers) { send(status, producer, contentType, headers.entrySet()); } @Override public final void send(int status, HttpContentProducer producer, String contentType, Iterable<? extends Map.Entry<String, String>> headers) { doSend(status, contentType, null, producer, createMultimap(headers)); } /** * Sub-class to implement on how to send a response. * * @param status response status code * @param contentType response content type * @param content response body if not null * @param contentProducer {@link HttpContentProducer} for producing the response body if not null; if this * is non-null, content should be null. * @param headers response headers */ protected abstract void doSend(int status, String contentType, @Nullable ChannelBuffer content, @Nullable HttpContentProducer contentProducer, @Nullable Multimap<String, String> headers); /** * Creates a {@link Multimap} from an {@link Iterable} of {@link Map.Entry}. */ protected final <K, V> Multimap<K, V> createMultimap(Iterable<? extends Map.Entry<K, V>> entries) { ImmutableMultimap.Builder<K, V> builder = ImmutableMultimap.builder(); for (Map.Entry<K, V> entry : entries) { builder.put(entry); } return builder.build(); } }