/*
* Created on 16 Jun 2006
* Created by Paul Gardner
* Copyright (C) 2006 Aelitis, 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; either version 2
* of the License, or (at your option) any later version.
* 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.
*
* AELITIS, SAS au capital de 46,603.30 euros
* 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
*
*/
package com.aelitis.azureus.core.networkmanager;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.SimpleTimer;
import org.gudy.azureus2.core3.util.SystemTime;
import org.gudy.azureus2.core3.util.TimerEvent;
import org.gudy.azureus2.core3.util.TimerEventPerformer;
import com.aelitis.azureus.core.networkmanager.Transport.ConnectListener;
public class
ConnectionEndpoint
{
private InetSocketAddress notional_address;
private ProtocolEndpoint[] protocols;
public
ConnectionEndpoint(
InetSocketAddress _notional_address )
{
notional_address = _notional_address;
}
public InetSocketAddress
getNotionalAddress()
{
return( notional_address );
}
public ProtocolEndpoint[]
getProtocols()
{
if ( protocols == null ){
return( new ProtocolEndpoint[0] );
}
return( protocols );
}
public void
addProtocol(
ProtocolEndpoint ep )
{
if ( protocols == null ){
protocols = new ProtocolEndpoint[]{ ep };
}else{
for (int i=0;i<protocols.length;i++){
if ( protocols[i] == ep ){
return;
}
}
ProtocolEndpoint[] new_ep = new ProtocolEndpoint[ protocols.length + 1 ];
System.arraycopy( protocols, 0, new_ep, 0, protocols.length );
new_ep[ protocols.length ] = ep;
protocols = new_ep;
}
ep.setConnectionEndpoint( this );
}
public ConnectionEndpoint
getLANAdjustedEndpoint()
{
ConnectionEndpoint result = new ConnectionEndpoint( notional_address );
for (int i=0;i<protocols.length;i++){
ProtocolEndpoint ep = protocols[i];
InetSocketAddress address = ep.getAdjustedAddress( true );
ProtocolEndpointFactory.createEndpoint( ep.getType(), result, address );
}
return( result );
}
public ConnectionAttempt
connectOutbound(
final boolean connect_with_crypto,
final boolean allow_fallback,
final byte[][] shared_secrets,
final ByteBuffer initial_data,
final int priority,
final ConnectListener listener )
{
if ( protocols.length == 1 ){
ProtocolEndpoint protocol = protocols[0];
final Transport transport = protocol.connectOutbound( connect_with_crypto, allow_fallback, shared_secrets, initial_data, priority, listener );
return(
new ConnectionAttempt()
{
public void
abandon()
{
if ( transport != null ){
transport.close( "Connection attempt abandoned" );
}
}
});
}else{
final boolean[] connected = { false };
final boolean[] abandoned = { false };
final List<Transport> transports = new ArrayList<Transport>( protocols.length );
final ConnectListener listener_delegate =
new ConnectListener()
{
//private long start_time;
private int timeout = Integer.MIN_VALUE;
private int fail_count;
public int
connectAttemptStarted(
int default_connect_timeout)
{
synchronized( connected ){
if ( timeout == Integer.MIN_VALUE ){
//start_time = SystemTime.getCurrentTime();
timeout = listener.connectAttemptStarted( default_connect_timeout );
}
return( timeout);
}
}
public void
connectSuccess(
Transport transport,
ByteBuffer remaining_initial_data )
{
boolean disconnect;
synchronized( connected ){
disconnect = abandoned[0];
if ( !disconnect ){
if ( !connected[0] ){
connected[0] = true;
//System.out.println( "Connect took " + (SystemTime.getCurrentTime() - start_time ) + " for " + transport.getDescription());
}else{
disconnect = true;
}
}
}
if ( disconnect ){
transport.close( "Transparent not required" );
}else{
listener.connectSuccess( transport, remaining_initial_data );
}
}
public void
connectFailure(
Throwable failure_msg )
{
boolean inform;
synchronized( connected ){
fail_count++;
inform = fail_count == protocols.length;
}
if ( inform ){
listener.connectFailure(failure_msg);
}
}
public Object
getConnectionProperty(
String property_name)
{
return( listener.getConnectionProperty( property_name ));
}
};
boolean ok = true;
if ( protocols.length != 2 ){
ok = false;
}else{
ProtocolEndpoint p1 = protocols[0];
ProtocolEndpoint p2 = protocols[1];
if ( p1.getType() == ProtocolEndpoint.PROTOCOL_TCP && p2.getType() == ProtocolEndpoint.PROTOCOL_UTP ){
}else if ( p2.getType() == ProtocolEndpoint.PROTOCOL_TCP && p1.getType() == ProtocolEndpoint.PROTOCOL_UTP ){
ProtocolEndpoint temp = p1;
p1 = p2;
p2 = temp;
}else{
ok = false;
}
if ( ok ){
// p2 is uTP, p1 is TCP
final ByteBuffer initial_data_copy;
if ( initial_data != null ){
initial_data_copy = initial_data.duplicate();
}else{
initial_data_copy = null;
}
Transport transport =
p2.connectOutbound(
connect_with_crypto,
allow_fallback,
shared_secrets,
initial_data,
priority,
new ConnectListenerEx( listener_delegate ));
transports.add( transport );
final ProtocolEndpoint tcp_ep = p1;
SimpleTimer.addEvent(
"delay:tcp:connect",
SystemTime.getCurrentTime() + 750,
false,
new TimerEventPerformer()
{
public void
perform(
TimerEvent event)
{
synchronized( connected ){
if ( connected[0] || abandoned[0] ){
return;
}
}
Transport transport =
tcp_ep.connectOutbound(
connect_with_crypto,
allow_fallback,
shared_secrets,
initial_data_copy,
priority,
new ConnectListenerEx( listener_delegate ));
synchronized( connected ){
if ( abandoned[0] ){
transport.close( "Connection attempt abandoned" );
}else{
transports.add( transport );
}
} }
});
}
}
if ( !ok ){
Debug.out( "No supportified!" );
listener.connectFailure( new Exception( "Not Supported" ));
}
return(
new ConnectionAttempt()
{
public void
abandon()
{
List<Transport> to_kill;
synchronized( connected ){
abandoned[0] = true;
to_kill = new ArrayList<Transport>( transports );
}
for ( Transport transport: to_kill ){
transport.close( "Connection attempt abandoned" );
}
}
});
}
}
public String
getDescription()
{
String str = "[";
for (int i=0;i<protocols.length;i++){
str += (i==0?"":",") + protocols[i].getDescription();
}
return( str + "]" );
}
private static class
ConnectListenerEx
implements ConnectListener
{
private ConnectListener listener;
private boolean ok;
private boolean failed;
private
ConnectListenerEx(
ConnectListener _listener )
{
listener = _listener;
}
public int
connectAttemptStarted(
int default_connect_timeout )
{
return( listener.connectAttemptStarted(default_connect_timeout));
}
public void
connectSuccess(
Transport transport,
ByteBuffer remaining_initial_data )
{
synchronized( this ){
if ( ok || failed ){
if ( ok ){
Debug.out( "Double doo doo" );
}
return;
}
ok = true;
}
listener.connectSuccess( transport, remaining_initial_data );
}
public void
connectFailure(
Throwable failure_msg )
{
synchronized( this ){
if ( ok || failed ){
return;
}
failed = true;
}
listener.connectFailure( failure_msg );
}
public Object
getConnectionProperty(
String property_name)
{
return( listener.getConnectionProperty( property_name ));
}
}
}