/*
* Created on Jul 18, 2004
* Created by Alon Rohter
* Copyright (C) 2004, 2005, 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.peermanager.utils;
import java.util.*;
import org.gudy.azureus2.core3.util.AEMonitor;
import com.aelitis.azureus.core.networkmanager.*;
import com.aelitis.azureus.core.peermanager.messaging.*;
import com.aelitis.azureus.core.peermanager.messaging.azureus.AZHave;
import com.aelitis.azureus.core.peermanager.messaging.azureus.AZMessage;
import com.aelitis.azureus.core.peermanager.messaging.bittorrent.*;
/**
* Utility class to enable write aggregation of BT Have messages,
* in order to save bandwidth by not wasting a whole network packet
* on a single small 9-byte message, and instead pad them onto other
* messages.
*/
public class OutgoingBTHaveMessageAggregator {
private final ArrayList pending_haves = new ArrayList();
private final AEMonitor pending_haves_mon = new AEMonitor( "OutgoingBTHaveMessageAggregator:PH");
private byte bt_have_version;
private byte az_have_version;
private boolean destroyed = false;
private final OutgoingMessageQueue outgoing_message_q;
private final OutgoingMessageQueue.MessageQueueListener added_message_listener = new OutgoingMessageQueue.MessageQueueListener() {
public boolean messageAdded( Message message ) { return true; }
public void messageQueued( Message message ) {
//if another message is going to be sent anyway, add our haves as well
String message_id = message.getID();
if( ! ( message_id.equals( BTMessage.ID_BT_HAVE ) || message_id.equals( AZMessage.ID_AZ_HAVE ))) {
sendPendingHaves();
}
}
public void messageRemoved( Message message ) {/*nothing*/}
public void messageSent( Message message ) {/*nothing*/}
public void protocolBytesSent( int byte_count ) {/*ignore*/}
public void dataBytesSent( int byte_count ) {/*ignore*/}
};
/**
* Create a new aggregator, which will send messages out the given queue.
* @param outgoing_message_q
*/
public
OutgoingBTHaveMessageAggregator(
OutgoingMessageQueue outgoing_message_q,
byte _bt_have_version,
byte _az_have_version )
{
this.outgoing_message_q = outgoing_message_q;
bt_have_version = _bt_have_version;
az_have_version = _az_have_version;
outgoing_message_q.registerQueueListener( added_message_listener );
}
public void
setHaveVersion(
byte bt_version,
byte az_version )
{
bt_have_version = bt_version;
az_have_version = az_version;
}
/**
* Queue a new have message for aggregated sending.
* @param piece_number of the have message
* @param force if true, send this and any other pending haves right away
*/
public void queueHaveMessage( int piece_number, boolean force ) {
if( destroyed ) return;
try{
pending_haves_mon.enter();
pending_haves.add( new Integer( piece_number ) );
if( force ) {
sendPendingHaves();
}
else {
int pending_bytes = pending_haves.size() * 9;
if( pending_bytes >= outgoing_message_q.getMssSize() ) {
//System.out.println("enough pending haves for a full packet!");
//there's enough pending bytes to fill a packet payload
sendPendingHaves();
}
}
}finally{
pending_haves_mon.exit();
}
}
/**
* Destroy the aggregator, along with any pending messages.
*/
public void destroy() {
try{
pending_haves_mon.enter();
pending_haves.clear();
destroyed = true;
}
finally{
pending_haves_mon.exit();
}
}
/**
* Force send of any aggregated/pending have messages.
*/
public void forceSendOfPending() {
sendPendingHaves();
}
/**
* Are there Haves messages pending?
* @return true if there are any unsent haves, false otherwise
*/
public boolean hasPending() { return !pending_haves.isEmpty(); }
private void
sendPendingHaves()
{
if ( destroyed ){
return;
}
try{
pending_haves_mon.enter();
int num_haves = pending_haves.size();
if ( num_haves == 0 ){
return;
}
// single have -> use BT
if ( num_haves == 1 || az_have_version < BTMessageFactory.MESSAGE_VERSION_SUPPORTS_PADDING ){
for( int i=0; i < num_haves; i++ ){
Integer piece_num = (Integer)pending_haves.get( i );
outgoing_message_q.addMessage( new BTHave( piece_num.intValue(), bt_have_version ), true );
}
}else{
int[] piece_numbers = new int[num_haves];
for( int i=0; i < num_haves; i++ ) {
piece_numbers[i] = ((Integer)pending_haves.get( i )).intValue();
}
outgoing_message_q.addMessage( new AZHave( piece_numbers, az_have_version ), true );
}
outgoing_message_q.doListenerNotifications();
pending_haves.clear();
}finally{
pending_haves_mon.exit();
}
}
}
|