Package org.ardverk.dht.io.transport

Source Code of org.ardverk.dht.io.transport.DatagramTransport

/*
* Copyright 2009-2012 Roger Kapsi
*
* 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 org.ardverk.dht.io.transport;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import org.ardverk.concurrent.DefaultExecutorQueue;
import org.ardverk.concurrent.ExecutorQueue;
import org.ardverk.concurrent.ExecutorUtils;
import org.ardverk.dht.KUID;
import org.ardverk.dht.codec.MessageCodec;
import org.ardverk.dht.codec.MessageCodec.Decoder;
import org.ardverk.dht.codec.MessageCodec.Encoder;
import org.ardverk.dht.codec.bencode.BencodeMessageCodec;
import org.ardverk.dht.message.Message;
import org.ardverk.dht.message.RequestMessage;
import org.ardverk.dht.message.ResponseMessage;
import org.ardverk.io.IoUtils;
import org.ardverk.net.NetworkUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* An implementation of {@link Transport} that uses {@link DatagramSocket}s.
*/
public class DatagramTransport extends AbstractTransport implements Closeable {

  private static final Logger LOG
    = LoggerFactory.getLogger(DatagramTransport.class);
 
  private static final ExecutorService EXECUTOR
    = ExecutorUtils.newCachedThreadPool("DatagramTransportThread");
 
  private final int MAX_SIZE = 8 * 1024;
 
  private final ExecutorQueue<Runnable> executor
    = new DefaultExecutorQueue(EXECUTOR);
 
  private final MessageCodec codec;
 
  private final SocketAddress bindaddr;
 
  private volatile DatagramSocket socket = null;
 
  private Future<?> future = null;
 
  private boolean open = true;
 
  public DatagramTransport(MessageCodec codec, int port) {
    this(codec, new InetSocketAddress(port));
  }
 
  public DatagramTransport(MessageCodec codec,
      String bindaddr, int port) {
    this(codec, new InetSocketAddress(bindaddr, port));
  }
 
  public DatagramTransport(MessageCodec codec,
      InetAddress bindaddr, int port) {
    this(codec, new InetSocketAddress(bindaddr, port));
  }
 
  public DatagramTransport(SocketAddress bindaddr) {
    this(new BencodeMessageCodec(), bindaddr);
  }
 
  public DatagramTransport(MessageCodec codec,
      SocketAddress bindaddr) {
    this.codec = codec;
    this.bindaddr = bindaddr;
  }
 
  @Override
  public SocketAddress getSocketAddress() {
    return bindaddr;
  }

  @Override
  public synchronized void bind(TransportCallback callback) throws IOException {
    if (!open) {
      throw new IOException();
    }
   
    super.bind(callback);

    socket = new DatagramSocket(bindaddr);

    Runnable task = new Runnable() {
      @Override
      public void run() {
        doServe();
      }
    };
   
    future = EXECUTOR.submit(task);
  }

  @Override
  public synchronized void unbind() {
    super.unbind();
   
    IoUtils.close(socket);
   
    if (future != null) {
      future.cancel(true);
    }
   
    synchronized (executor) {
      executor.getQueue().clear();
    }
  }

  @Override
  public synchronized void close() {
    open = false;
    executor.shutdownNow();
    unbind();
  }
 
  private void doServe() {
   
    byte[] buffer = new byte[MAX_SIZE];
    DatagramPacket packet
      = new DatagramPacket(buffer, buffer.length);
   
    DatagramSocket socket = null;
    while ((socket = this.socket) != null
        && !socket.isClosed()) {
     
      try {
        packet.setData(buffer);
        socket.receive(packet);
       
        process(packet);
      } catch (IOException err) {
        uncaughtException(socket, err);
      }
    }
  }
 
  private void process(DatagramPacket packet) {
   
    final SocketAddress src = packet.getSocketAddress();
    final byte[] data = extract(packet);
   
    Runnable task = new Runnable() {
      @Override
      public void run() {
        Decoder decoder = null;
        try {
          decoder = codec.createDecoder(src,
              new ByteArrayInputStream(data));
          Message message = decoder.read();
         
          if (message instanceof RequestMessage) {
            handleRequest((RequestMessage)message);
          } else {
            handleResponse((ResponseMessage)message);
          }
         
        } catch (IOException err) {
          uncaughtException(socket, err);
        } finally {
          IoUtils.close(decoder);
        }
      }
     
      private void handleRequest(RequestMessage request) throws IOException {
        ResponseMessage response = DatagramTransport.this.handleRequest(request);
        if (response != null) {
          KUID contactId = request.getContact().getId();
          send(contactId, response, -1L, TimeUnit.MILLISECONDS);
        }
      }
     
      private boolean handleResponse(ResponseMessage response) throws IOException {
        return DatagramTransport.this.handleResponse(response);
      }
    };
   
    executor.execute(task);
    //EXECUTOR.execute(task);
  }
 
  @Override
  public void send(final KUID contactId, final Message message,
      long timeout, TimeUnit unit) throws IOException {
   
    final DatagramSocket socket = this.socket;
    if (socket == null || socket.isClosed()) {
      throw new IOException();
    }
   
    Runnable task = new Runnable() {
      @Override
      public void run() {
        try {
         
          SocketAddress addr = message.getAddress();
          SocketAddress endpoint = NetworkUtils.getResolved(addr);
         
          ByteArrayOutputStream baos = new ByteArrayOutputStream();
          Encoder encoder = codec.createEncoder(baos);
          encoder.write(message);
          encoder.close();
         
          byte[] encoded = baos.toByteArray();
          DatagramPacket packet = new DatagramPacket(
              encoded, 0, encoded.length, endpoint);
         
          socket.send(packet);
          messageSent(contactId, message);
         
        } catch (IOException err) {
          uncaughtException(socket, err);
          handleException(message, err);
        }
      }
    };
   
    executor.execute(task);
    //EXECUTOR.execute(task);
  }
 
  protected void uncaughtException(DatagramSocket socket, Throwable t) {
    if (socket.isClosed()) {
      LOG.debug("Exception", t);
    } else {
      LOG.error("Exception", t);
    }
  }
 
  /**
   * Extracts and returns a copy of the {@link DatagramPacket}'s {@code byte[]}.
   *
   * @see DatagramPacket#getData()
   */
  private static byte[] extract(DatagramPacket packet) {
    byte[] data = packet.getData();
    int offset = packet.getOffset();
    int length = packet.getLength();
   
    byte[] copy = new byte[length];
    System.arraycopy(data, offset, copy, 0, copy.length);
   
    return copy;
  }
}
TOP

Related Classes of org.ardverk.dht.io.transport.DatagramTransport

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.