Package co.cask.cdap.internal.app.runtime.procedure

Source Code of co.cask.cdap.internal.app.runtime.procedure.HttpProcedureResponder

/*
* Copyright © 2014 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.procedure;

import co.cask.cdap.api.procedure.ProcedureResponse;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.gson.Gson;
import com.google.gson.stream.JsonWriter;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBufferOutputStream;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.jboss.netty.handler.codec.http.HttpVersion;

import java.io.IOException;
import java.io.OutputStreamWriter;

/**
*
*/
final class HttpProcedureResponder extends AbstractProcedureResponder {

  private final Channel channel;
  private ProcedureResponse.Writer writer;

  HttpProcedureResponder(Channel channel) {
    this.channel = channel;
  }

  @Override
  public synchronized ProcedureResponse.Writer stream(ProcedureResponse response) throws IOException {
    if (writer != null) {
      return writer;
    }

    HttpResponse httpResponse = createHttpResponse(response);
    if (!httpResponse.getStatus().equals(HttpResponseStatus.OK)) {
      handleFailure(httpResponse);
      return writer;
    }

    httpResponse.setHeader(HttpHeaders.Names.TRANSFER_ENCODING, HttpHeaders.Values.CHUNKED);
    httpResponse.setHeader(HttpHeaders.Names.CONTENT_TYPE, "application/octet-stream");
    try {
      channel.write(httpResponse).await();
    } catch (InterruptedException e) {
      throw new IOException(e);
    }
    writer = new HttpResponseWriter(channel);

    return writer;
  }

  @Override
  public synchronized void sendJson(ProcedureResponse response, Object object) throws IOException {
    if (writer != null) {
      throw new IOException("A writer is already opened for streaming or the response was already sent.");
    }

    try {
      HttpResponse httpResponse = createHttpResponse(response);
      if (!httpResponse.getStatus().equals(HttpResponseStatus.OK)) {
        handleFailure(httpResponse);
        return;
      }

      ChannelBuffer channelBuffer = ChannelBuffers.dynamicBuffer();
      JsonWriter jsonWriter = new JsonWriter(new OutputStreamWriter(new ChannelBufferOutputStream(channelBuffer),
                                                                    Charsets.UTF_8));
      new Gson().toJson(object, object.getClass(), jsonWriter);
      jsonWriter.close();

      httpResponse.setContent(channelBuffer);
      httpResponse.setHeader(HttpHeaders.Names.CONTENT_TYPE, "application/json");
      httpResponse.setHeader(HttpHeaders.Names.CONTENT_LENGTH, channelBuffer.readableBytes());
      ChannelFuture result = channel.write(httpResponse);
      result.addListener(ChannelFutureListener.CLOSE);
      result.await();
    } catch (Throwable t) {
      throw new IOException(t);
    } finally {
      writer = ResponseWriters.CLOSED_WRITER;
    }
  }

  @Override
  public synchronized void error(ProcedureResponse.Code errorCode, String errorMessage) throws IOException {
    Preconditions.checkArgument(errorCode != ProcedureResponse.Code.SUCCESS, "Cannot send SUCCESS as error.");

    if (writer != null) {
      throw new IOException("A writer is already opened for streaming or the response was already sent.");
    }

    try {
      ChannelBuffer errorContent = ChannelBuffers.wrappedBuffer(Charsets.UTF_8.encode(errorMessage));
      HttpResponse httpResponse = createHttpResponse(new ProcedureResponse(errorCode));
      httpResponse.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/plain");
      httpResponse.setHeader(HttpHeaders.Names.CONTENT_LENGTH, errorContent.readableBytes());
      httpResponse.setContent(errorContent);
      channel.write(httpResponse).addListener(ChannelFutureListener.CLOSE);
    } finally {
      writer = ResponseWriters.CLOSED_WRITER;
    }
  }

  private void handleFailure(HttpResponse httpResponse) {
    httpResponse.setContent(ChannelBuffers.EMPTY_BUFFER);
    channel.write(httpResponse).addListener(ChannelFutureListener.CLOSE);
  }

  private HttpResponse createHttpResponse(ProcedureResponse response) throws IOException {
    Preconditions.checkNotNull(response, "ProcedureResponse cannot be null.");
    HttpResponseStatus status;
    switch (response.getCode()) {
      case SUCCESS:
        status = HttpResponseStatus.OK;
        break;
      case FAILURE:
        status = HttpResponseStatus.INTERNAL_SERVER_ERROR;
        break;
      case CLIENT_ERROR:
        status = HttpResponseStatus.BAD_REQUEST;
        break;
      case NOT_FOUND:
        status = HttpResponseStatus.NOT_FOUND;
        break;
      default:
        status = HttpResponseStatus.INTERNAL_SERVER_ERROR;
    }

    return new DefaultHttpResponse(HttpVersion.HTTP_1_1, status);
  }
}
TOP

Related Classes of co.cask.cdap.internal.app.runtime.procedure.HttpProcedureResponder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.