/* * Created on 29-Jul-2004 * Created by Paul Gardner * 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 org.gudy.azureus2.core3.tracker.server.impl; /** * @author parg * */ import java.util.*; import java.net.*; import org.gudy.azureus2.core3.tracker.server.*; import org.gudy.azureus2.core3.config.*; import org.gudy.azureus2.core3.util.*; import org.gudy.azureus2.core3.logging.*; import com.aelitis.azureus.core.proxy.AEProxyFactory; public class TRTrackerServerNATChecker { private static final LogIDs LOGID = LogIDs.TRACKER; protected static TRTrackerServerNATChecker singleton = new TRTrackerServerNATChecker(); protected static final int THREAD_POOL_SIZE = 32; protected static final int CHECK_QUEUE_LIMIT = 2048; protected static int check_timeout = TRTrackerServer.DEFAULT_NAT_CHECK_SECS*1000; protected static TRTrackerServerNATChecker getSingleton() { return( singleton ); } protected boolean enabled; protected ThreadPool thread_pool; protected List check_queue = new ArrayList(); protected AESemaphore check_queue_sem = new AESemaphore("TracerServerNATChecker"); protected AEMonitor check_queue_mon = new AEMonitor( "TRTrackerServerNATChecker:Q" ); protected AEMonitor this_mon = new AEMonitor( "TRTrackerServerNATChecker" ); protected TRTrackerServerNATChecker() { final String enable_param = "Tracker NAT Check Enable"; final String timeout_param = "Tracker NAT Check Timeout"; final String[] params = { enable_param, timeout_param }; for (int i=0;i<params.length;i++){ COConfigurationManager.addParameterListener( params[i], new ParameterListener() { public void parameterChanged( String parameter_name) { checkConfig( enable_param, timeout_param ); } }); } checkConfig( enable_param, timeout_param ); } protected boolean isEnabled() { return( enabled ); } protected void checkConfig( String enable_param, String timeout_param ) { try{ this_mon.enter(); enabled = COConfigurationManager.getBooleanParameter( enable_param ); check_timeout = COConfigurationManager.getIntParameter( timeout_param ) * 1000; if ( check_timeout < 1000 ){ Debug.out( "NAT check timeout too small - " + check_timeout ); check_timeout = 1000; } if ( thread_pool == null ){ thread_pool = new ThreadPool("Tracker NAT Checker", THREAD_POOL_SIZE ); thread_pool.setExecutionLimit( check_timeout ); Thread dispatcher_thread = new AEThread( "Tracker NAT Checker Dispatcher" ) { public void runSupport() { while(true){ check_queue_sem.reserve(); ThreadPoolTask task; try{ check_queue_mon.enter(); task = (ThreadPoolTask)check_queue.remove(0); }finally{ check_queue_mon.exit(); } try{ thread_pool.run( task ); }catch( Throwable e ){ Debug.printStackTrace( e ); } } } }; dispatcher_thread.setDaemon( true ); dispatcher_thread.start(); }else{ thread_pool.setExecutionLimit( check_timeout ); } }finally{ this_mon.exit(); } } protected boolean addNATCheckRequest( final String host, final int port, final TRTrackerServerNatCheckerListener listener ) { if ((!enabled) || thread_pool == null ){ return( false ); } try{ check_queue_mon.enter(); if ( check_queue.size() > CHECK_QUEUE_LIMIT ){ if (Logger.isEnabled()) Logger.log(new LogEvent(LOGID, LogEvent.LT_WARNING, "NAT Check queue size too large, check for '" + host + ":" + port + "' skipped")); //Debug.out( "NAT Check queue size too large, check skipped" ); listener.NATCheckComplete( true ); }else{ check_queue.add( new ThreadPoolTask() { protected Socket socket; public void runSupport() { boolean ok = false; try{ InetSocketAddress address = new InetSocketAddress( AEProxyFactory.getAddressMapper().internalise(host), port ); socket = new Socket(); socket.connect( address, check_timeout ); ok = true; socket.close(); socket = null; }catch( Throwable e ){ }finally{ listener.NATCheckComplete( ok ); if ( socket != null ){ try{ socket.close(); }catch( Throwable e ){ } } } } public void interruptTask() { if ( socket != null ){ try{ socket.close(); }catch( Throwable e ){ } } } }); check_queue_sem.release(); } }finally{ check_queue_mon.exit(); } return( true ); } }