Package com.webobjects.appserver

Source Code of com.webobjects.appserver.WONettyAdaptor$RequestHandler

package com.webobjects.appserver;

import static org.jboss.netty.channel.Channels.pipeline;
import static org.jboss.netty.handler.codec.http.HttpHeaders.isKeepAlive;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.concurrent.Executors;

import org.apache.log4j.Logger;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
import org.jboss.netty.handler.codec.http.HttpHeaders.Names;
import org.jboss.netty.handler.codec.http.HttpHeaders.Values;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
import org.jboss.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import org.jboss.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import org.jboss.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import org.jboss.netty.handler.codec.http.websocketx.WebSocketFrame;
import org.jboss.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import org.jboss.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;

import com.webobjects.appserver._private.WOProperties;
import com.webobjects.foundation.NSDelayedCallbackCenter;
import com.webobjects.foundation.NSDictionary;
import com.webobjects.foundation.NSNotificationCenter;

import er.woadaptor.ERWOAdaptorUtilities;
import er.woadaptor.websockets.WebSocket;
import er.woadaptor.websockets.WebSocketFactory;
import er.woadaptor.websockets.WebSocketStore;

/**
* How to use the WONettyAdaptor:
*
* 1. Build/Install ERWOAdaptor framework
* 2. Include ERWOAdaptor framework in your app/project
* 3. Run your app with the property:
*   -WOAdaptor er.woadaptor.ERWOAdaptor
*
*  OR:
*   -WOAdaptor WONettyAdaptor
*
* 4. (Optional) If developing with the WONettyAdaptor set the following properties as well:
*
*   -WODirectConnectEnabled false
*  
*  AND (maybe)
*  
*   -WOAllowRapidTurnaround false
*
* @author ravim
* @author ramsey (WebSocket support)
*
* @version 2.0
*/
public class WONettyAdaptor extends WOAdaptor {

    private static final Logger log = Logger.getLogger(WONettyAdaptor.class);
   
    private int _port;
    private String _hostname;
   
    private ChannelFactory channelFactory;
    private Channel channel;
   
    private String hostname() {
      if (_hostname == null) {
        try {
          InetAddress _host = InetAddress.getLocalHost();
        _hostname = _host.getHostName();
      } catch (UnknownHostException exception) {
        log.error("Failed to get localhost address");
      }
      }
      return _hostname;
    }

  public WONettyAdaptor(String name, NSDictionary<String, Object> config) {
        super(name, config);

        Number number = (Number) config.objectForKey(WOProperties._PortKey);
        if (number != null)
            _port = number.intValue();
        if (_port < 0)
            _port = 0;
       
        _hostname = (String) config.objectForKey(WOProperties._HostKey);
        WOApplication.application()._setHost(hostname());
  }

  @Override
  public void registerForEvents() {
    // Configure the server.
    channelFactory = new NioServerSocketChannelFactory(
        Executors.newCachedThreadPool(),
        Executors.newCachedThreadPool());
    ServerBootstrap bootstrap = new ServerBootstrap(channelFactory);

    // Set up the event pipeline factory.
    bootstrap.setPipelineFactory(new PipelineFactory());

    // Bind and start to accept incoming connections.
    channel = bootstrap.bind(new InetSocketAddress(hostname(), _port));
   
    log.debug("Binding adaptor to address: " + channel.getLocalAddress());
    _port = ((InetSocketAddress) channel.getLocalAddress()).getPort();
    System.setProperty(WOProperties._PortKey, Integer.toString(_port));
  }

  @Override
  public void unregisterForEvents() {
    ChannelFuture future = channel.close();
    future.awaitUninterruptibly();
    channelFactory.releaseExternalResources();
  }
   
  @Override
  public int port() {
    return _port;
  }
 
  @Override
  public boolean dispatchesRequestsConcurrently() {
    return true;
  }
 
  /**
    * Originally inspired by:
    *
    * @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
    * @author Andy Taylor (andy.taylor@jboss.org)
    * @author <a href="http://gleamynode.net/">Trustin Lee</a>
    *
    * @see <a href="http://docs.jboss.org/netty/3.2/xref/org/jboss/netty/example/http/snoop/HttpServerPipelineFactory.html">HttpServerPipelineFactory</a>
    *
    * @author ravim   ERWOAdaptor/WONettyAdaptor
    *
    * @property WOMaxIOBufferSize     Max http chunking size. Defaults to WO default 8196
    *                   @see <a href="http://docs.jboss.org/netty/3.2/xref/org/jboss/netty/handler/codec/http/HttpMessageDecoder.html">HttpMessageDecoder</a>
    *
    * @property WOFileUpload.sizeLimit  Max file upload size permitted
    */
  protected static class PipelineFactory implements ChannelPipelineFactory {
   
    // TODO ravi: CHECKME Netty default is 8192; WO default is 8196(!?)
    public final Integer maxChunkSize = Integer.getInteger("WOMaxIOBufferSize", 8196);
    public final Integer maxFileSize = Integer.getInteger("WOFileUpload.sizeLimit", 1024*1024*100);
   
    public ChannelPipeline getPipeline() throws Exception {
      // Create a default pipeline implementation.
      ChannelPipeline pipeline = pipeline();

      pipeline.addLast("decoder", new HttpRequestDecoder(4096, 8192, maxChunkSize));
      pipeline.addLast("aggregator", new HttpChunkAggregator(maxFileSize));
      pipeline.addLast("encoder", new HttpResponseEncoder());
      pipeline.addLast("handler", new RequestHandler());
      return pipeline;
    }
  }

  /**
   * Originally inspired by:
   *
   * @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
   * @author Andy Taylor (andy.taylor@jboss.org)
   * @author <a href="http://gleamynode.net/">Trustin Lee</a>
   *
   * @see <a href="http://docs.jboss.org/netty/3.2/xref/org/jboss/netty/example/http/snoop/HttpRequestHandler.html">HttpRequestHandler</a>
   *
   * @author ravim   ERWOAdaptor/WONettyAdaptor version
   */
  protected static class RequestHandler extends SimpleChannelUpstreamHandler {
   
    private static final Logger log = Logger.getLogger(RequestHandler.class);
   
    private WebSocketServerHandshaker handshaker;

    @Override
    public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
      super.channelClosed(ctx, e);
      NSNotificationCenter.defaultCenter().postNotification(WebSocketStore.CHANNEL_CLOSED_NOTIFICATION, ctx.getChannel());
    }
   
    /**
     * @see <a href="http://docs.jboss.org/netty/3.2/api/org/jboss/netty/channel/SimpleChannelUpstreamHandler.html#messageReceived(org.jboss.netty.channel.ChannelHandlerContext,%20org.jboss.netty.channel.MessageEvent)">SimpleChannelUpstreamHandler</a>
     */
    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
      Object msg = e.getMessage();
      if(msg instanceof WebSocketFrame) {
        handleWebSocketFrame(ctx, e, (WebSocketFrame)msg);
      } else if(msg instanceof HttpRequest) {
        handleHTTPRequest(ctx, e, (HttpRequest)msg);
      }
    }
   
    protected void handleWebSocketFrame(ChannelHandlerContext ctx, MessageEvent e, WebSocketFrame frame) {
      WebSocket socket = WebSocketStore.defaultWebSocketStore().socketForChannel(e.getChannel());
          if (frame instanceof CloseWebSocketFrame) {
            //TODO remove from store?
              handshaker.close(ctx.getChannel(), (CloseWebSocketFrame) frame);
          } else if (frame instanceof PingWebSocketFrame) {
              ctx.getChannel().write(new PongWebSocketFrame(frame.getBinaryData()));
          } else if(socket != null) {
        socket.receiveFrame(frame);
      }
    }
   
    protected void handleHTTPRequest(ChannelHandlerContext ctx, MessageEvent e, HttpRequest _request) throws IOException {
     
      if(_request.getHeader(Names.SEC_WEBSOCKET_VERSION) != null ||
          (Values.UPGRADE.equalsIgnoreCase(_request.getHeader(Names.CONNECTION)) && Values.WEBSOCKET.equalsIgnoreCase(_request.getHeader(Names.UPGRADE)))
          ) {
       
        handleUpgradeRequest(ctx, _request);
      } else {
       
        WORequest worequest = ERWOAdaptorUtilities.asWORequest(_request);
        worequest._setOriginatingAddress(((InetSocketAddress) ctx.getChannel().getRemoteAddress()).getAddress());
        WOResponse woresponse = WOApplication.application().dispatchRequest(worequest);
 
        // send a response
        NSDelayedCallbackCenter.defaultCenter().eventEnded();
 
        // Decide whether to close the connection or not.
        boolean keepAlive = isKeepAlive(_request);
 
        //For reasons that escape me, empty responses fail to close properly.
        boolean close = !(woresponse._contentLength() > 0 || woresponse.contentInputStream() != null);

        // Write the response.
        HttpResponse response = ERWOAdaptorUtilities.asHttpResponse(woresponse);
        ChannelFuture future = e.getChannel().write(response);
 
        // Close the non-keep-alive connection after the write operation is done.
        if (close || !keepAlive) {
          future.addListener(ChannelFutureListener.CLOSE);
        }
      }
    }
   
    protected void handleUpgradeRequest(ChannelHandlerContext ctx, HttpRequest req) {
      //If factory doesn't exist, close the upgrade request channel
      WebSocketFactory factory = WebSocketStore.defaultWebSocketStore().factory();
      if(factory == null) {
        ctx.getChannel().close();
        return;
      }
     
      WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
                  ERWOAdaptorUtilities.getWebSocketLocation(req), null, false);
      handshaker = wsFactory.newHandshaker(req);
     
      Channel socketChannel = ctx.getChannel();
      if(handshaker == null) {
        wsFactory.sendUnsupportedWebSocketVersionResponse(socketChannel);
      } else {
        ChannelFuture future = handshaker.handshake(socketChannel, req);
        //TODO tie this to the channel future result?
        //Create a WebSocket instance to handle frames
        WebSocket socket = factory.create(socketChannel, req);
        WebSocketStore.defaultWebSocketStore().takeSocketForChannel(socket, socketChannel);
       
        socket.didUpgrade();
      }
    }

    /**
     * @see <a href="http://docs.jboss.org/netty/3.2/api/org/jboss/netty/channel/SimpleChannelUpstreamHandler.html#exceptionCaught(org.jboss.netty.channel.ChannelHandlerContext,%20org.jboss.netty.channel.ExceptionEvent)">SimpleChannelUpstreamHandler</a>
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
          Throwable cause = e.getCause();

      log.warn(cause.getMessage());
      e.getChannel().close();
    }
  }
}
TOP

Related Classes of com.webobjects.appserver.WONettyAdaptor$RequestHandler

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.