Package com.linkedin.databus2.core.container.netty

Source Code of com.linkedin.databus2.core.container.netty.ChunkedBodyWritableByteChannel

package com.linkedin.databus2.core.container.netty;
/*
*
* Copyright 2013 LinkedIn Corp. All rights reserved
*
* 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.
*
*/


import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import org.apache.log4j.Logger;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelConfig;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.socket.nio.NioSocketChannelConfig;
import org.jboss.netty.handler.codec.http.DefaultHttpChunk;
import org.jboss.netty.handler.codec.http.DefaultHttpChunkTrailer;
import org.jboss.netty.handler.codec.http.HttpChunk;
import org.jboss.netty.handler.codec.http.HttpChunkTrailer;
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 com.linkedin.databus2.core.container.ChunkedWritableByteChannel;

public class ChunkedBodyWritableByteChannel implements ChunkedWritableByteChannel
{
  public static final String MODULE = ChunkedBodyWritableByteChannel.class.getName();
  public static final Logger LOG = Logger.getLogger(MODULE);

  public static final String RESPONSE_CODE_FOOTER_NAME = "x-databus-response-code";

  private final Channel _channel;

  /** Temporarily stores the HTTP response headers until the first byte of the body is sent.
   * This lets response producers to set headers. If set to null, the response headers have been
   * sent.
   */
  private HttpResponse _response;
  private HttpResponseStatus _responseCode;
  private boolean _open = true;
  private HttpChunk _chunkReuse = null;
  private HttpChunkTrailer _trailer = null;


  public ChunkedBodyWritableByteChannel(Channel channel, HttpResponse response)
  {
    _channel = channel;
    _responseCode = response.getStatus();
    _response = response;
    ChannelConfig channelConfig = _channel.getConfig();
    if (channelConfig instanceof NioSocketChannelConfig)
    {
      NioSocketChannelConfig nioSocketConfig = (NioSocketChannelConfig)channelConfig;
      //FIXME: add a config for the high water-mark
      nioSocketConfig.setWriteBufferLowWaterMark(16 * 1024);
      nioSocketConfig.setWriteBufferHighWaterMark(64 * 1024);
    }
  }

  @Override
  public int write(ByteBuffer buffer) throws IOException
  {
    if (null != _response)
    {
      _response.setHeader(HttpHeaders.Names.TRANSFER_ENCODING, HttpHeaders.Values.CHUNKED);
      _response.setChunked(true);
      writeToChannel(_response);
      _response = null;
    }

    //We need to lie to netty that the buffer is BIG_ENDIAN event if it is LITTLE_ENDIAN (see DDS-1212)
    ByteOrder bufferOrder = buffer.order();
    ByteBuffer realBuffer = bufferOrder == ByteOrder.BIG_ENDIAN
        ? buffer : buffer.slice().order(ByteOrder.BIG_ENDIAN);
    if (null == _chunkReuse)
    {
      _chunkReuse = new DefaultHttpChunk(ChannelBuffers.wrappedBuffer(realBuffer));
    }
    else
    {
      _chunkReuse.setContent(ChannelBuffers.wrappedBuffer(realBuffer));
    }

    int bytesWritten = realBuffer.remaining();
    writeToChannel(_chunkReuse)//We assume this either writes the whole buffer or else throws (no partial writes)

    //DDSDBUS-1363: WritableByteChannel.write() contract requires both (1) return of bytes written and (2) update of
    //position. Without the latter, NIO loops forever.
    realBuffer.position(realBuffer.limit());
    //DDS-1212
    if (bufferOrder == ByteOrder.LITTLE_ENDIAN) buffer.position(realBuffer.position());

    return bytesWritten;
  }

  @Override
  public void close() throws IOException
  {
    if (_open)
    {
      _open = false;
      if (null != _response)
      {
        _response.setHeader(HttpHeaders.Names.CONTENT_LENGTH,
                            Long.valueOf(_response.getContent().readableBytes()));

        writeToChannel(_response, 10);
        _response = null;
      }
      else
      {
        if (null != _trailer)
        {
          writeToChannel(_trailer, 1);
          _trailer = null;
        }
        else
        {
          writeToChannel(HttpChunk.LAST_CHUNK, 1);
        }
      }
    }
  }

  @Override
  public void addMetadata(String name, Object value)
  {
    if (null != _response)
    {
      _response.addHeader(name, value);
    }
    else
    {
      if (null == _trailer)
      {
        _trailer = new DefaultHttpChunkTrailer();
      }

      _trailer.addHeader(name, value);
    }
  }

  @Override
  public void setMetadata(String name, Object value)
  {
    if (null != _response)
    {
      _response.setHeader(name, value);
    }
    else
    {
      if (null == _trailer)
      {
        _trailer = new DefaultHttpChunkTrailer();
      }

      _trailer.setHeader(name, value);
    }
  }

  @Override
  public void removeMetadata(String name)
  {
    if (null != _response)
    {
      _response.removeHeader(name);
    }
    else if (null != _trailer)
    {
      _trailer.removeHeader(name);
    }
  }
  private void writeToChannel(Object o, int flushSize) throws IOException
  {
    ChannelFuture channelFuture = _channel.write(o);
    if (flushSize > 0 && !_channel.isWritable())
    {
      ChannelConfig channelConfig = _channel.getConfig();
      if (channelConfig instanceof NioSocketChannelConfig)
      {
        NioSocketChannelConfig nioSocketConfig = (NioSocketChannelConfig)channelConfig;
        nioSocketConfig.setWriteBufferLowWaterMark(flushSize);
        nioSocketConfig.setWriteBufferHighWaterMark(flushSize);
      }
    }
    awaitChannelFuture(channelFuture);
    if (! channelFuture.isSuccess())
    {
      throw new IOException(channelFuture.getCause());
    }
  }

  private void writeToChannel(Object o) throws IOException
  {
    writeToChannel(o, 0);
  }

  private void awaitChannelFuture(ChannelFuture channelFuture)
  {
    boolean done = channelFuture.isDone();
    while (!done)
    {
      try
      {
        channelFuture.await();
        done = channelFuture.isDone();
      }
      catch (InterruptedException ie)
      {//do nothing
      }
    }
}

  @Override
  public boolean isOpen()
  {
    return _open;
  }


  @Override
  public HttpResponseStatus getResponseCode()
  {
    return _responseCode;
  }

  @Override
  public void setResponseCode(HttpResponseStatus responseCode)
  {
    _responseCode = responseCode;

    if (null != _response)
    {
      _response.setStatus(responseCode);
    }
    else
    {
      setMetadata(RESPONSE_CODE_FOOTER_NAME, Integer.toString(responseCode.getCode()));
    }
  }

  @Override
  public Channel getRawChannel()
  {
  return _channel;
  }
}
TOP

Related Classes of com.linkedin.databus2.core.container.netty.ChunkedBodyWritableByteChannel

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.