/*
* Created on Jan 23, 2013
* Created by Paul Gardner
*
* Copyright 2013 Azureus Software, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License only.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
package com.vuze.client.plugins.utp.loc;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.*;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.HostNameToIPResolver;
import com.vuze.client.plugins.utp.UTPProvider;
import com.vuze.client.plugins.utp.UTPProviderCallback;
import com.vuze.client.plugins.utp.UTPProviderException;
//import com.vuze.client.plugins.utp.loc.v1.UTPTranslatedV1;
import com.vuze.client.plugins.utp.loc.v2.UTPTranslatedV2;
public class
UTPProviderLocal
implements UTPProvider,
UTPTranslated.SendToProc,
UTPTranslated.UTPGotIncomingConnection,
UTPTranslated.UTPFunctionTable
{
private static final int version = 2;
private boolean test_mode;
private UTPTranslated impl;
private UTPProviderCallback callback;
private long socket_id_next;
private Map<Long,UTPSocket> socket_map = new HashMap<Long, UTPSocket>();
private Map<Integer,Integer> pending_options = new HashMap<Integer, Integer>();
public
UTPProviderLocal()
{
this( false );
}
public
UTPProviderLocal(
boolean _test_mode )
{
test_mode = _test_mode;
}
public boolean
load(
UTPProviderCallback _callback )
{
synchronized( pending_options ){
if ( impl != null ){
Debug.out( "Already loaded ");
return( false );
}
callback = _callback;
if ( version == 1 ){
//impl = new UTPTranslatedV1( callback, test_mode );
}else{
impl = new UTPTranslatedV2( callback, this, this, this, test_mode );
}
if ( pending_options.size() > 0 ){
for ( Map.Entry<Integer,Integer> entry: pending_options.entrySet()){
setOption( entry.getKey(), entry.getValue());
}
pending_options.clear();
}
return( true );
}
}
public int
getVersion()
{
return( version );
}
public boolean
isValidPacket(
byte[] data,
int length )
{
return( impl.isValidPacket( data, length ));
}
// callbacks from implementation
public void
send_to_proc(
Object user_data,
byte[] data,
InetSocketAddress addr )
{
callback.send( addr, data, data.length );
}
public void
got_incoming_connection(
Object user_data,
UTPSocket socket )
{
long socket_id;
synchronized( socket_map ){
socket_id = socket_id_next++;
socket_map.put( socket_id, socket );
//System.out.println( "socket_map: " + socket_map.size());
}
InetSocketAddress[] addr_out = {null};
impl.UTP_GetPeerName( socket, addr_out );
callback.incomingConnection( addr_out[0], socket_id, impl.UTP_GetSocketConnectionID( socket ) & 0x0000ffffL );
try{
if ( version == 1 ){
impl.UTP_SetCallbacks( socket, this, new Object[]{ socket_id, socket });
}else{
impl.UTP_SetUserData( socket, new Object[]{ socket_id, socket });
}
}catch( Throwable e ){
Debug.out( e );
}
}
public void
on_read(
Object user_data,
byte[] bytes,
int count )
{
long socket_id = (Long)((Object[])user_data)[0];
callback.read( socket_id, bytes );
}
public void
on_read(
Object user_data,
ByteBuffer bytes,
int count )
{
long socket_id = (Long)((Object[])user_data)[0];
callback.read( socket_id, bytes );
}
public void
on_write(
Object user_data,
byte[] bytes,
int offset,
int length )
{
long socket_id = (Long)((Object[])user_data)[0];
callback.write( socket_id, bytes, offset, length );
}
public int
get_rb_size(
Object user_data )
{
long socket_id = (Long)((Object[])user_data)[0];
return( callback.getReadBufferSize( socket_id ));
}
public void
on_state(
Object user_data,
int state )
{
long socket_id = (Long)((Object[])user_data)[0];
callback.setState(socket_id, state);
if ( state == UTPTranslated.UTP_STATE_DESTROYING ){
synchronized( socket_map ){
socket_map.remove( socket_id);
//System.out.println( "socket_map: " + socket_map.size());
}
}
}
public void
on_error(
Object user_data,
int errcode )
{
long socket_id = (Long)((Object[])user_data)[0];
callback.error( socket_id, errcode );
}
public void
on_overhead(
Object user_data,
boolean send,
int count,
int type)
{
long socket_id = (Long)((Object[])user_data)[0];
callback.overhead( socket_id, send, count, type );
}
// incoming calls from Vuze
public void
checkTimeouts()
{
impl.UTP_CheckTimeouts();
}
public void
incomingIdle()
{
impl.UTP_IncomingIdle();
}
public long[]
connect(
String to_address,
int to_port )
throws UTPProviderException
{
try{
UTPSocket socket;
long socket_id;
if ( version == 1 ){
socket = impl.UTP_Create( this, "", new InetSocketAddress( HostNameToIPResolver.syncResolve( to_address), to_port ));
if ( socket == null ){
throw( new UTPProviderException( "Failed to create socket" ));
}
synchronized( socket_map ){
socket_id = socket_id_next++;
socket_map.put( socket_id, socket );
//System.out.println( "socket_map: " + socket_map.size());
}
impl.UTP_SetCallbacks( socket, this, new Object[]{ socket_id, socket });
impl.UTP_Connect( socket );
}else{
socket = impl.UTP_Create();
if ( socket == null ){
throw( new UTPProviderException( "Failed to create socket" ));
}
synchronized( socket_map ){
socket_id = socket_id_next++;
socket_map.put( socket_id, socket );
//System.out.println( "socket_map: " + socket_map.size());
}
impl.UTP_SetUserData( socket, new Object[]{ socket_id, socket });
impl.UTP_Connect( socket, new InetSocketAddress( HostNameToIPResolver.syncResolve( to_address), to_port ));
}
return( new long[]{ socket_id, impl.UTP_GetSocketConnectionID( socket ) & 0x0000ffffL });
}catch( UTPProviderException e ){
throw( e );
}catch( Throwable e ){
throw( new UTPProviderException( "connect failed", e ));
}
}
public boolean
receive(
String from_address,
int from_port,
byte[] data,
int length )
throws UTPProviderException
{
try{
return( impl.UTP_IsIncomingUTP(this, this, "", data, length, new InetSocketAddress( HostNameToIPResolver.syncResolve( from_address), from_port )));
}catch( Throwable e ){
throw( new UTPProviderException( "receive failed", e ));
}
}
public boolean
write(
long utp_socket,
int avail_bytes )
throws UTPProviderException
{
UTPSocket socket;
synchronized( socket_map ){
socket = socket_map.get( utp_socket );
}
if ( socket != null ){
return( impl.UTP_Write( socket, avail_bytes ));
}
throw( new UTPProviderException( "Unknown socket" ));
}
public boolean
write(
long utp_socket,
ByteBuffer[] buffers,
int start,
int len )
throws UTPProviderException
{
UTPSocket socket;
synchronized( socket_map ){
socket = socket_map.get( utp_socket );
}
if ( socket != null ){
return( impl.UTP_Write( socket, buffers, start, len ));
}
throw( new UTPProviderException( "Unknown socket" ));
}
public void
receiveBufferDrained(
long utp_socket )
throws UTPProviderException
{
UTPSocket socket;
synchronized( socket_map ){
socket = socket_map.get( utp_socket );
}
if ( socket != null ){
impl.UTP_RBDrained( socket );
}else{
throw( new UTPProviderException( "Unknown socket" ));
}
}
public void
close(
long utp_socket )
throws UTPProviderException
{
UTPSocket socket;
synchronized( socket_map ){
socket = socket_map.remove( utp_socket );
//System.out.println( "socket_map: " + socket_map.size());
}
if ( socket != null ){
impl.UTP_Close( socket );
}
}
public void
setSocketOptions(
long fd )
throws UTPProviderException
{
// this is supposed to be a native method to enable/disable fragmentation
}
public void
setOption(
int option,
int value )
{
synchronized( pending_options ){
if ( impl == null ){
pending_options.put( option, value );
}else{
impl.UTP_SetOption( option, value );
}
}
}
}