Package org.hornetq.core.remoting.impl.netty

Source Code of org.hornetq.core.remoting.impl.netty.HttpAcceptorHandler

/*
* Copyright 2009 Red Hat, Inc.
* Red Hat licenses this file to you 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 org.hornetq.core.remoting.impl.netty;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.ReferenceCountUtil;

/**
* Ensures that every request has a response and also that any uninitiated responses always wait for
* a response.
* @author <a href="mailto:andy.taylor@jboss.org">Andy Taylor</a>
*/
public class HttpAcceptorHandler extends ChannelDuplexHandler
{
   private final BlockingQueue<ResponseHolder> responses = new LinkedBlockingQueue<ResponseHolder>();

   private final BlockingQueue<Runnable> delayedResponses = new LinkedBlockingQueue<Runnable>();

   private final ExecutorService executor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS, delayedResponses);

   private final HttpKeepAliveRunnable httpKeepAliveTask;

   private final long responseTime;

   private Channel channel;

   public HttpAcceptorHandler(final HttpKeepAliveRunnable httpKeepAliveTask, final long responseTime, Channel channel)
   {
      super();
      this.responseTime = responseTime;
      this.httpKeepAliveTask = httpKeepAliveTask;
      this.channel = channel;
      httpKeepAliveTask.registerKeepAliveHandler(this);
   }

   @Override
   public void channelInactive(final ChannelHandlerContext ctx) throws Exception
   {
      super.channelInactive(ctx);
      httpKeepAliveTask.unregisterKeepAliveHandler(this);
      channel = null;
   }

   @Override
   public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception
   {
      FullHttpRequest request = (FullHttpRequest) msg;
      HttpMethod method = request.getMethod();
      // if we are a post then we send upstream, otherwise we are just being prompted for a response.
      if (method.equals(HttpMethod.POST ) )
      {
         ctx.fireChannelRead(ReferenceCountUtil.retain(((FullHttpRequest) msg).content()));
         // add a new response
         responses.put(new ResponseHolder(System.currentTimeMillis() + responseTime,
               new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK)));
         ReferenceCountUtil.release(msg);
         return;
      }
      super.channelRead(ctx, msg);
   }

   @Override
   public void write(final ChannelHandlerContext ctx, final Object msg, ChannelPromise promise) throws Exception
   {
      // we are either a channel buffer, which gets delayed until a response is available, or we are the actual response
      if (msg instanceof ByteBuf)
      {
         executor.execute(new ResponseRunner((ByteBuf) msg, promise));
      }
      else
      {
         ctx.write(msg, promise);
      }
   }

   public void keepAlive(final long time)
   {
      // send some responses to catch up thus avoiding any timeout.
      int lateResponses = 0;
      for (ResponseHolder response : responses)
      {
         if (response.timeReceived < time)
         {
            lateResponses++;
         }
         else
         {
            break;
         }
      }
      for (int i = 0; i < lateResponses; i++)
      {
         executor.execute(new ResponseRunner());
      }
   }

   /**
    * this is prompted to delivery when a response is available in the response queue.
    */
   final class ResponseRunner implements Runnable
   {
      private final ByteBuf buffer;

      private final boolean bogusResponse;

      private final ChannelPromise promise;

      public ResponseRunner(final ByteBuf buffer, ChannelPromise promise)
      {
         this.buffer = buffer;
         bogusResponse = false;
         this.promise = promise;
      }

      public ResponseRunner()
      {
         bogusResponse = true;
         buffer = Unpooled.buffer(0);
         promise = channel.newPromise();
      }

      public void run()
      {
         ResponseHolder responseHolder = null;
         do
         {
            try
            {
               responseHolder = responses.take();
            }
            catch (InterruptedException e)
            {
               if (executor.isShutdown())
                  return;
               // otherwise ignore, we'll just try again
            }
         }
         while (responseHolder == null);
         if (!bogusResponse)
         {
            piggyBackResponses(responseHolder.response.content());
            responseHolder.response.headers().set(HttpHeaders.Names.CONTENT_LENGTH,
                                              String.valueOf(responseHolder.response.content().readableBytes()));
            channel.writeAndFlush(responseHolder.response, promise);
         }
         else
         {
            responseHolder.response.content().writeBytes(buffer);
            responseHolder.response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(responseHolder.response.content().readableBytes()));
            channel.writeAndFlush(responseHolder.response, promise);
         }

         buffer.release();

      }

      // TODO: This can be optimized a lot
      private void piggyBackResponses(ByteBuf buf)
      {
         // if we are the last available response then we have to piggy back any remaining responses
         if (responses.isEmpty())
         {
            buf.writeBytes(buffer);
            do
            {
               try
               {
                  ResponseRunner responseRunner = (ResponseRunner)delayedResponses.poll(0, TimeUnit.MILLISECONDS);
                  if (responseRunner == null)
                  {
                     break;
                  }
                  buf.writeBytes(responseRunner.buffer);
                  responseRunner.buffer.release();
               }
               catch (InterruptedException e)
               {
                  break;
               }
            }
            while (responses.isEmpty());
            return;
         }
         buf.writeBytes(buffer);
      }

   }


   public void shutdown()
   {
      executor.shutdown();
      try
      {
         executor.awaitTermination(10, TimeUnit.SECONDS);
      }
      catch (InterruptedException e)
      {
         // no-op
      }
      finally
      {
         executor.shutdownNow();
      }
      responses.clear();
   }

   /**
    * a holder class so we know what time  the request first arrived
    */
   private static final class ResponseHolder
   {
      final FullHttpResponse response;

      final long timeReceived;

      public ResponseHolder(final long timeReceived, final FullHttpResponse response)
      {
         this.timeReceived = timeReceived;
         this.response = response;
      }
   }

}
TOP

Related Classes of org.hornetq.core.remoting.impl.netty.HttpAcceptorHandler

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.