Package org.menacheri.jetclient

Source Code of org.menacheri.jetclient.NettyUDPClient

package org.menacheri.jetclient;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
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.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.socket.DatagramChannel;
import org.jboss.netty.channel.socket.DatagramChannelFactory;
import org.jboss.netty.channel.socket.nio.NioDatagramChannelFactory;
import org.menacheri.jetclient.app.Session;
import org.menacheri.jetclient.event.Events;
import org.menacheri.jetclient.event.Event;
import org.menacheri.jetclient.handlers.netty.UDPUpstreamHandler;

/**
* This client class is used for UDP communication with a remote jetserver. Same
* client instance can be used to create multiple UDP "connections" to same
* jetserver. For connecting with multiple jetserver's use multiple instances of
* this class.
*
* @author Abraham Menacherry
*
*/
public class NettyUDPClient
{
  /**
   * The remote server address to which this client should connect.
   */
  private final InetSocketAddress serverAddress;
  /**
   * The worker executor which will provide threads to Netty
   * {@link ChannelFactory} for decoding encoding done on the
   * {@link ChannelPipeline}.
   */
  private final ExecutorService worker;
  private final ConnectionlessBootstrap udpBootstrap;
  /**
   * The instance of {@link NioDatagramChannelFactory} created by constructor,
   * or the one passed in to constructor.
   */
  private final DatagramChannelFactory channelFactory;
  /**
   * For UDP there can only be one pipelineFactory per
   * {@link ConnectionlessBootstrap}. This factory is hence part of the client
   * class.
   */
  private final ChannelPipelineFactory pipelineFactory;
  /**
   * This map is used to store the local address to which a session has bound
   * itself using the {@link DatagramChannel#bind(java.net.SocketAddress)}
   * method. When an incoming UDP packet is recieved the
   * {@link UDPUpstreamHandler} will resolve which session to pass the event,
   * using this map.
   */
  public static final Map<InetSocketAddress, Session> CLIENTS = new HashMap<InetSocketAddress, Session>();

  /**
   * Creates an instance of a Netty UDP client which can then be used to
   * connect to a remote jet-server. This constructor delegates to
   * {@link #NettyUDPClient(InetSocketAddress, ChannelPipelineFactory)}
   * constructor after creating a {@link InetSocketAddress} instance based on
   * the host and port number passed in.
   *
   * @param jetserverHost
   *            The host name of the remote server on which jetserver is
   *            running.
   * @param port
   *            The port to connect to, on the remote server.
   * @param pipelineFactory
   *            The pipeline factory to be used while creating a Netty
   *            {@link Channel}
   * @throws UnknownHostException
   * @throws Exception
   */
  public NettyUDPClient(String jetserverHost, int port,
      final ChannelPipelineFactory pipelineFactory)
      throws UnknownHostException, Exception
  {
    this(new InetSocketAddress(jetserverHost, port), pipelineFactory);
  }

  public NettyUDPClient(final InetSocketAddress serverAddress,
      final ChannelPipelineFactory pipelineFactory)
      throws UnknownHostException, Exception
  {
    this(serverAddress, pipelineFactory, null, Executors
        .newCachedThreadPool());
  }

  /**
   * Creates a new instance of the {@link NettyUDPClient}.
   *
   * @param serverAddress
   *            The remote servers address. This address will be used when any
   *            of the default write/connect methods are used.
   * @param pipelineFactory
   *            The Netty factory used for creating a pipeline. For UDP, this
   *            pipeline factory should not have any stateful i.e non
   *            share-able handlers in it. Since Netty only has one channel
   *            for <b>ALL</b> UPD traffic.
   * @param channelFactory
   *            <b>Can be provided as null</b>. If so, it will by default use
   *            {@link NioDatagramChannelFactory}. If not null, then the
   *            provided factory is set.
   * @param worker
   *            The executor used for creating worker threads. Can be null if
   *            channelFactory parameter is <b>Not</b> null.
   * @throws UnknownHostException
   */
  public NettyUDPClient(final InetSocketAddress serverAddress,
      final ChannelPipelineFactory pipelineFactory,
      final DatagramChannelFactory channelFactory,
      final ExecutorService worker) throws UnknownHostException,
      Exception
  {
    this.worker = worker;
    this.serverAddress = serverAddress;
    if (channelFactory == null)
    {
      this.channelFactory = new NioDatagramChannelFactory(worker);
    }
    else
    {
      this.channelFactory = channelFactory;
    }
    this.udpBootstrap = new ConnectionlessBootstrap(this.channelFactory);
    udpBootstrap.setOption("broadcast", "true");
    this.pipelineFactory = pipelineFactory;
    // The pipeline factory should not be set on the udpBootstrap since it
    // invalidates the getPipeline.
    udpBootstrap.setPipeline(pipelineFactory.getPipeline());
    Runtime.getRuntime().addShutdownHook(new Thread()
    {
      public void run()
      {
        udpBootstrap.releaseExternalResources();
      }
    });
  }

  /**
   * Creates a new datagram channel instance using the {@link #udpBootstrap}
   * by binding to local host. This method delegates to
   * {@link #createDatagramChannel(String)} internally, by passing the
   * localhost's host name to it.
   *
   * @return The newly created instance of the datagram channel.
   * @throws UnknownHostException
   */
  public DatagramChannel createDatagramChannel() throws UnknownHostException
  {
    return createDatagramChannel(InetAddress.getLocalHost()
        .getHostAddress());
  }

  /**
   * Creates a new datagram channel instance using the {@link #udpBootstrap}
   * by binding to local host.
   *
   * @param localhostName
   *            The host machine (for e.g. 'localhost') to which it needs to
   *            bind to. This is <b>Not</b> the remote jet-server hostname.
   * @return The newly created instance of the datagram channel.
   * @throws UnknownHostException
   */
  public DatagramChannel createDatagramChannel(String localhostName)
      throws UnknownHostException
  {
    DatagramChannel datagramChannel = (DatagramChannel) udpBootstrap
        .bind(new InetSocketAddress(localhostName, 0));
    return datagramChannel;
  }

  /**
   * This method will connect the datagram channel with the server and send
   * the {@link Events#CONNECT} message to server. This method will use
   * {@link #serverAddress} by default when sending the
   * {@link Events#CONNECT} message. <b>Note</b> Even if this connect
   * message does not reach server, the first UDP message that the server
   * receives from this particular DatagramChannels local address will be
   * converted by server and used as {@link Events#CONNECT}.
   *
   * @param session
   *            The session for which the datagram channel is being created.
   * @param datagramChannel
   *            The channel on which the message is to be sent to remote
   *            server.
   * @return Returns a ChannelFuture which can be used to check the success of
   *         this operation. <b>NOTE</b> Success in case of UDP means message
   *         is sent to server. It does not mean that the server has received
   *         it.
   * @throws UnknownHostException
   */
  public ChannelFuture connect(Session session,
      DatagramChannel datagramChannel) throws UnknownHostException,
      InterruptedException
  {
    return connect(session, datagramChannel, this.serverAddress, 5,
        TimeUnit.SECONDS);
  }

  /**
   * This method will connect the datagram channel with the server and send
   * the {@link Events#CONNECT} message to server.
   *
   * @param session
   *            The session for which the datagram channel is being created.
   * @param datagramChannel
   *            The channel on which the message is to be sent to remote
   *            server.
   * @param serverAddress
   *            The remote address of the server to which to send this
   *            message.
   * @param timeout
   *            Amount of time to wait for the connection to happen.
   *            <b>NOTE</b> Since this is UDP there is actually no "real"
   *            connection.
   * @return Returns a ChannelFuture which can be used to check the success of
   *         this operation. <b>NOTE</b> Success in case of UDP means message
   *         is sent to server. It does not mean that the server has received
   *         it.
   * @throws UnknownHostException
   */
  public ChannelFuture connect(Session session,
      DatagramChannel datagramChannel, InetSocketAddress serverAddress,
      int timeout, TimeUnit unit) throws UnknownHostException,
      InterruptedException
  {
    if (null == datagramChannel)
    {
      throw new NullPointerException(
          "DatagramChannel passed to connect method cannot be null");
    }
    if (!datagramChannel.isBound())
    {
      throw new IllegalStateException("DatagramChannel: "
          + datagramChannel
          + " Passed to connect method is not bound");
    }

    Event event = Events.event(null, Events.CONNECT);
    ChannelFuture future = datagramChannel.write(event, serverAddress);
    future.addListener(new ChannelFutureListener()
    {
      @Override
      public void operationComplete(ChannelFuture future)
          throws Exception
      {
        if (!future.isSuccess())
        {
          throw new RuntimeException(future.getCause());
        }
      }
    });
    CLIENTS.put(datagramChannel.getLocalAddress(), session);
    return future;
  }

  /**
   * Utility method used to send a message to the server. Users can also use
   * datagramChannel.write(message, serverAddress) directly. This method
   * delegates to {@link #write(DatagramChannel, Object, InetSocketAddress)}
   * by passing in the InetSocketAddress stored in the class variable
   * {@link #serverAddress}
   *
   * @param datagramChannel
   *            The channel on which the message is to be sent to remote
   *            server.
   * @param message
   *            The message to be sent. <b>NOTE</b> The message should be a
   *            valid and encode-able by the encoders in the ChannelPipeline
   *            of this server.
   * @return Returns a ChannelFuture which can be used to check the success of
   *         this operation. <b>NOTE</b> Success in case of UDP means message
   *         is sent to server. It does not mean that the server has received
   *         it.
   */
  public ChannelFuture write(DatagramChannel datagramChannel, Object message)
  {
    return write(datagramChannel, message, serverAddress);
  }

  /**
   * Utility method used to send a message to the server. Users can also use
   * datagramChannel.write(message, serverAddress) directly.
   *
   * @param datagramChannel
   *            The channel on which the message is to be sent to remote
   *            server.
   * @param message
   *            The message to be sent. <b>NOTE</b> The message should be a
   *            valid and encode-able by the encoders in the ChannelPipeline
   *            of this server.
   * @return Returns a ChannelFuture which can be used to check the success of
   *         this operation. <b>NOTE</b> Success in case of UDP means message
   *         is sent to server. It does not mean that the server has received
   *         it.
   */
  public static ChannelFuture write(DatagramChannel datagramChannel, Object message,
      InetSocketAddress serverAddress)
  {
    return datagramChannel.write(message, serverAddress);
  }

  public InetSocketAddress getLocalAddress(DatagramChannel c)
  {
    InetSocketAddress add = (InetSocketAddress) c.getLocalAddress();
    return add;
  }

  public InetSocketAddress getServerAddress()
  {
    return serverAddress;
  }

  public ExecutorService getWorker()
  {
    return worker;
  }

  public ConnectionlessBootstrap getUdpBootstrap()
  {
    return udpBootstrap;
  }

  public DatagramChannelFactory getChannelFactory()
  {
    return channelFactory;
  }

  public ChannelPipelineFactory getPipelineFactory()
  {
    return pipelineFactory;
  }

}
TOP

Related Classes of org.menacheri.jetclient.NettyUDPClient

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.