FileDocCategorySizeDatePackage
AZInstanceManagerImpl.javaAPI DocAzureus 3.0.3.427238Fri Sep 07 08:44:10 BST 2007com.aelitis.azureus.core.instancemanager.impl

AZInstanceManagerImpl

public class AZInstanceManagerImpl extends Object implements com.aelitis.net.udp.mc.MCGroupAdapter, com.aelitis.azureus.core.instancemanager.AZInstanceManager

Fields Summary
private static final boolean
DISABLE_LAN_LOCAL_STUFF
private static final org.gudy.azureus2.core3.logging.LogIDs
LOGID
private String
MC_GROUP_ADDRESS
private int
MC_GROUP_PORT
private int
MC_CONTROL_PORT
private static final int
MT_VERSION
private static final int
MT_ALIVE
private static final int
MT_BYE
private static final int
MT_REQUEST
private static final int
MT_REPLY
private static final int
MT_REQUEST_SEARCH
private static final int
MT_REQUEST_TRACK
private static final long
ALIVE_PERIOD
private static AZInstanceManagerImpl
singleton
private List
listeners
private static org.gudy.azureus2.core3.util.AEMonitor
class_mon
private static String
socks_proxy
private com.aelitis.azureus.core.AzureusCore
core
private com.aelitis.net.udp.mc.MCGroup
mc_group
private long
search_id_next
private List
requests
private AZMyInstanceImpl
my_instance
private Map
other_instances
private volatile Map
tcp_lan_to_ext
private volatile Map
udp_lan_to_ext
private volatile Map
udp2_lan_to_ext
private volatile Map
tcp_ext_to_lan
private volatile Map
udp_ext_to_lan
private volatile Map
udp2_ext_to_lan
private volatile Set
lan_addresses
private volatile Set
ext_addresses
private volatile List
lan_subnets
private volatile List
explicit_peers
private volatile boolean
include_well_known_lans
private org.gudy.azureus2.core3.util.AESemaphore
initial_search_sem
private org.gudy.azureus2.core3.util.AEMonitor
this_mon
private boolean
closing
Constructors Summary
protected AZInstanceManagerImpl(com.aelitis.azureus.core.AzureusCore _core)

	
	
	
			 
	
		core			= _core;
		
		my_instance	= new AZMyInstanceImpl( core, this );
		
		new AZPortClashHandler( this );
	
Methods Summary
protected voidaddAddresses(com.aelitis.azureus.core.instancemanager.AZInstance inst)

		InetAddress	internal_address 	= inst.getInternalAddress();
		InetAddress	external_address	= inst.getExternalAddress();
		int			tcp					= inst.getTCPListenPort();
		int			udp					= inst.getUDPListenPort();
		int			udp2				= inst.getUDPNonDataListenPort();
		
		modifyAddresses( internal_address, external_address, tcp, udp, udp2, true );
	
public booleanaddInstance(java.net.InetAddress explicit_address)

		return( addInstanceSupport( explicit_address, true ));
	
protected booleanaddInstanceSupport(java.net.InetAddress explicit_address, boolean force_send_alive)

		final InetSocketAddress	sad = new InetSocketAddress( explicit_address, MC_GROUP_PORT );
		
		boolean	new_peer = false;
		
		if ( !explicit_peers.contains( sad )){
			
			try{
				this_mon.enter();
	
				List	new_peers = new ArrayList( explicit_peers );
				
				new_peers.add( sad );
				
				explicit_peers	= new_peers;
				
			}finally{
				
				this_mon.exit();
			}
							
			new_peer = true;

		}
		
		if ( force_send_alive || new_peer ){
			
				// take this off the current thread as there are potential deadlock issues
				// regarding this during initialisation as sending the event attempts to
				// get the external address, this may hit DHT and the current thread
				// maybe initialising the DHT...
			
			new DelayedEvent(
					"AZInstanceManagerImpl:delaySendAlive", 
					0,
					new AERunnable()
					{
						public void 
						runSupport()
						{
							sendAlive( sad );
						}
					});
		}
		
		return( new_peer );
	
public booleanaddLANSubnet(java.lang.String subnet)

		String	str = "";
		
		for (int i=0;i<subnet.length();i++){
			
			char	c = subnet.charAt(i);
			
			if ( c == '*" ){
				
				str += ".*?";
				
			}else if ( c == '." ){
				
				str += "\\.";
			
			}else{
				
				str += c;
			}
		}
		
		Pattern pattern = Pattern.compile( str );
		
		for (int i=0;i<lan_subnets.size();i++){
			
			if ( pattern.pattern().equals(((Pattern)lan_subnets.get(i)).pattern())){
				
				return( false );
			}
		}
		
		try{
			this_mon.enter();
			
			List	new_nets = new ArrayList( lan_subnets );
			
			new_nets.add( pattern );
			
			lan_subnets	= new_nets;
			
		}finally{
			
			this_mon.exit();
		}
		
		return( true );
	
public voidaddListener(com.aelitis.azureus.core.instancemanager.AZInstanceManagerListener l)

		listeners.add( l );
	
protected AZOtherInstanceImplcheckAdd(AZOtherInstanceImpl inst)

		if ( inst.getID().equals( my_instance.getID())){
			
			return( inst );
		}
		
		boolean	added 	= false;
		boolean	changed	= false;
		
		try{
			this_mon.enter();
			
			AZOtherInstanceImpl	existing = (AZOtherInstanceImpl)other_instances.get( inst.getID());
			
			if ( existing == null ){
				
				added	= true;
			
				other_instances.put( inst.getID(), inst );
								
			}else{
								
				changed = existing.update( inst );

				inst	= existing;
			}
		}finally{
			
			this_mon.exit();
		}
		
		if ( added ){
			
			informAdded( inst );
			
		}else if ( changed ){
			
			informChanged( inst );
		}
		
		return( inst );
	
protected voidcheckRemove(AZOtherInstanceImpl inst)

		if ( inst.getID().equals( my_instance.getID())){
			
			return;
		}
		
		boolean	removed = false;
		
		try{
			this_mon.enter();
			
			removed = other_instances.remove( inst.getID()) != null;
			
		}finally{
			
			this_mon.exit();
		}
		
		if ( removed ){
			
			informRemoved( inst );
		}
	
protected voidcheckTimeouts()

		long	now = SystemTime.getCurrentTime();
	
		List	removed = new ArrayList();
		
		try{
			this_mon.enter();

			Iterator	it = other_instances.values().iterator();
			
			while( it.hasNext()){
				
				AZOtherInstanceImpl	inst = (AZOtherInstanceImpl)it.next();
	
				if ( now - inst.getAliveTime() > ALIVE_PERIOD * 2.5 ){
					
					removed.add( inst );
					
					it.remove();
				}
			}
		}finally{
			
			this_mon.exit();
		}
		
		for (int i=0;i<removed.size();i++){
			
			AZOtherInstanceImpl	inst = (AZOtherInstanceImpl)removed.get(i);
			
			informRemoved( inst );
		}
	
public java.net.InetSocketAddressgetExternalAddress(java.net.InetSocketAddress lan_address, int address_type)

		Map	map;
		
		if ( address_type == AT_TCP ){
			map = tcp_lan_to_ext;
		}else if ( address_type == AT_UDP ){
			map = udp_lan_to_ext;
		}else{
			map = udp2_lan_to_ext;
		}
		
		if ( map.size() == 0 ){
			
			return( null );
		}
		
		return((InetSocketAddress)map.get( lan_address ));	
	
public booleangetIncludeWellKnownLANs()

		return( include_well_known_lans );
	
public java.net.InetSocketAddressgetLANAddress(java.net.InetSocketAddress external_address, int address_type)

		Map	map;
		
		if ( address_type == AT_TCP ){
			map = tcp_ext_to_lan;
		}else if ( address_type == AT_UDP ){
			map = udp_ext_to_lan;
		}else{
			map = udp2_ext_to_lan;
		}
		
		if ( map.size() == 0 ){
			
			return( null );
		}
		
		return((InetSocketAddress)map.get( external_address ));
	
public com.aelitis.azureus.core.instancemanager.AZInstancegetMyInstance()

		return( my_instance );
	
public com.aelitis.azureus.core.instancemanager.AZInstance[]getOtherInstances()

		initial_search_sem.reserve();
		
		try{
			this_mon.enter();

			return((AZInstance[])other_instances.values().toArray( new AZInstance[other_instances.size()]));
			
		}finally{
			
			this_mon.exit();
		}
	
public static com.aelitis.azureus.core.instancemanager.AZInstanceManagergetSingleton(com.aelitis.azureus.core.AzureusCore core)

	
	
		COConfigurationManager.addAndFireParameterListeners(
			new String[]{ "Proxy.Data.Enable", "Proxy.Host", "Proxy.Data.Same", "Proxy.Data.Host" },
			new ParameterListener()
			{
				public void 
				parameterChanged(
					String parameterName )
				{
					if ( !COConfigurationManager.getBooleanParameter("Proxy.Data.Enable")){
						
						socks_proxy = null;
						
						return;
					}
					
					if ( COConfigurationManager.getBooleanParameter("Proxy.Data.Same")){
						
						socks_proxy = COConfigurationManager.getStringParameter( "Proxy.Host" );
						
					}else{
						
						socks_proxy = COConfigurationManager.getStringParameter( "Proxy.Data.Host" );

					}
					
					if ( socks_proxy != null ){
						
						socks_proxy	= socks_proxy.trim();
					}
				}
			});
	
		try{
			class_mon.enter();
			
			if ( singleton == null ){
				
				singleton = new AZInstanceManagerImpl( core );
			}
		}finally{
			
			class_mon.exit();
		}
		
		return( singleton );
	
protected voidinformAdded(com.aelitis.azureus.core.instancemanager.AZInstance inst)

		addAddresses( inst );

		for (int i=0;i<listeners.size();i++){
			
			try{
				((AZInstanceManagerListener)listeners.get(i)).instanceFound( inst );
				
			}catch( Throwable e ){
				
				Debug.printStackTrace(e);
			}
		}
	
protected voidinformChanged(com.aelitis.azureus.core.instancemanager.AZInstance inst)

		addAddresses( inst );
		
		if ( inst == my_instance ){
			
			sendAlive();
		}
		
		for (int i=0;i<listeners.size();i++){
			
			try{
				((AZInstanceManagerListener)listeners.get(i)).instanceChanged( inst );
				
			}catch( Throwable e ){
				
				Debug.printStackTrace(e);
			}
		}
	
protected voidinformRemoved(AZOtherInstanceImpl inst)

		removeAddresses( inst );
		
		for (int i=0;i<listeners.size();i++){
			
			try{
				((AZInstanceManagerListener)listeners.get(i)).instanceLost( inst );
				
			}catch( Throwable e ){
				
				Debug.printStackTrace(e);
			}
		}
	
protected voidinformTracked(com.aelitis.azureus.core.instancemanager.AZInstanceTracked inst)

		for (int i=0;i<listeners.size();i++){
			
			try{
				((AZInstanceManagerListener)listeners.get(i)).instanceTracked( inst );
				
			}catch( Throwable e ){
				
				Debug.printStackTrace(e);
			}
		}
	
public voidinitialize()

		
		try{
			mc_group = 
				MCGroupFactory.getSingleton(
					this,
					MC_GROUP_ADDRESS,
					MC_GROUP_PORT,
					MC_CONTROL_PORT,
					null );
					
			core.addLifecycleListener(
				new AzureusCoreLifecycleAdapter()
				{
					public void
					stopping(
						AzureusCore		core )
					{
						closing	= true;
						
						sendByeBye();
					}
				});
			
			SimpleTimer.addPeriodicEvent(
				"InstManager:timeouts",
				ALIVE_PERIOD,
				new TimerEventPerformer()
				{
					public void
					perform(
						TimerEvent	event )
					{
						checkTimeouts();
													
						sendAlive();				
					}
				});
		
		}catch( Throwable e ){
			
			initial_search_sem.releaseForever();
			
			Debug.printStackTrace(e);
		}
		
		new AEThread( "AZInstanceManager:initialSearch", true )
		{
			public void
			runSupport()
			{
				try{
					search();
					
						// pick up our own details as soon as we can
					
					addAddresses( my_instance );
					
				}finally{
					
					initial_search_sem.releaseForever();
				}
			}
		}.start();
	
public voidinterfaceChanged(java.net.NetworkInterface network_interface)

		sendAlive();
	
protected booleanisClosing()

		return( closing );
	
public booleanisExternalAddress(java.net.InetAddress address)

		return( ext_addresses.contains( address ));
	
public booleanisInitialized()

		return( initial_search_sem.isReleasedForever());
	
public booleanisLANAddress(java.net.InetAddress address)

		if ( DISABLE_LAN_LOCAL_STUFF ){
			
			return( false );
		}
		
		if ( address == null ){
			
			return( false );
		}
		
		String	sp = socks_proxy;
		
		if ( sp != null ){
			
			if ( sp.equals( address.getHostAddress())){
				
				return( false );
			}
		}
		
		if ( include_well_known_lans ){
		
			if ( 	address.isLoopbackAddress() || 
					address.isLinkLocalAddress() ||
					address.isSiteLocalAddress()){
					
				return( true );
			}
		}
		
		String	host_address = address.getHostAddress();
		
		for (int i=0;i<lan_subnets.size();i++){
			
			Pattern	p = (Pattern)lan_subnets.get(i);
			
			if ( p.matcher( host_address ).matches()){
								
				return( true );
			}
		}
		
		if ( lan_addresses.contains( address )){
			
			return( true );
		}
		
		if ( explicit_peers.size() > 0 ){
			
			Iterator	it = explicit_peers.iterator();
			
			while( it.hasNext()){

				if (((InetSocketAddress)it.next()).getAddress().equals( address )){
					
					return( true );
				}
			}
		}
		
		return( false );
	
public voidlog(java.lang.Throwable e)

		Debug.printStackTrace(e);
	
protected java.util.MapmodifyAddress(java.util.Map map, java.net.InetSocketAddress key, java.net.InetSocketAddress value, boolean add)

		// System.out.println( "ModAddress: " + key + " -> " + value + " - " + (add?"add":"remove"));
		
		InetSocketAddress	old_value = (InetSocketAddress)map.get(key);

		boolean	same = old_value != null && old_value.equals( value );
		
		Map	new_map = map;
		
		if ( add ){
			
			if ( !same ){
				
				new_map	= new HashMap( map );
	
				new_map.put( key, value );
			}
		}else{
			
			if ( same ){
				
				new_map	= new HashMap( map );
				
				new_map.remove( key );
			}
		}	
		
		return( new_map );
	
protected voidmodifyAddresses(java.net.InetAddress internal_address, java.net.InetAddress external_address, int tcp, int udp, int udp2, boolean add)

		if ( internal_address.isAnyLocalAddress()){
			
			try{
				internal_address = NetUtils.getLocalHost();
				
			}catch( Throwable e ){
				
				Debug.printStackTrace(e);
			}
		}
		
		try{
			this_mon.enter();
 
			InetSocketAddress	int_tcp = new InetSocketAddress(internal_address, tcp);
			InetSocketAddress	ext_tcp = new InetSocketAddress(external_address, tcp);
			InetSocketAddress	int_udp = new InetSocketAddress(internal_address, udp);
			InetSocketAddress	ext_udp = new InetSocketAddress(external_address, udp);
			InetSocketAddress	int_udp2 = new InetSocketAddress(internal_address, udp2);
			InetSocketAddress	ext_udp2 = new InetSocketAddress(external_address, udp2);

				// not the most efficient code in the world this... will need rev
			
			tcp_ext_to_lan 	= modifyAddress( tcp_ext_to_lan, ext_tcp, int_tcp, add );
			tcp_lan_to_ext 	= modifyAddress( tcp_lan_to_ext, int_tcp, ext_tcp, add );
			udp_ext_to_lan 	= modifyAddress( udp_ext_to_lan, ext_udp, int_udp, add );
			udp_lan_to_ext 	= modifyAddress( udp_lan_to_ext, int_udp, ext_udp, add );
			udp2_ext_to_lan = modifyAddress( udp2_ext_to_lan, ext_udp2, int_udp2, add );
			udp2_lan_to_ext = modifyAddress( udp2_lan_to_ext, int_udp2, ext_udp2, add );

			if ( !lan_addresses.contains( internal_address )){
				
				Set	new_lan_addresses = new HashSet( lan_addresses );
				
				new_lan_addresses.add( internal_address );
				
				lan_addresses	= new_lan_addresses;
			}
			
			if ( !ext_addresses.contains( external_address )){
				
				Set	new_ext_addresses = new HashSet( ext_addresses );
				
				new_ext_addresses.add( external_address );
				
				ext_addresses	= new_ext_addresses;
			}
		}finally{
			
			this_mon.exit();
		}
	
public voidreceived(java.net.NetworkInterface network_interface, java.net.InetAddress local_address, java.net.InetSocketAddress originator, byte[] data, int length)

		try{
			Map	map = BDecoder.decode(new BufferedInputStream(new ByteArrayInputStream( data, 0, length )));
			
			long	version = ((Long)map.get( "ver" )).longValue();
			long	type	= ((Long)map.get( "type" )).longValue();
			
			InetAddress	originator_address = originator.getAddress();
			
			if ( map.get( "explicit" ) != null ){
				
				addInstanceSupport( originator_address, false );
			}
			
			AZOtherInstanceImpl	instance = AZOtherInstanceImpl.decode( originator_address, (Map)map.get( "orig" ));
			
			if ( type == MT_ALIVE ){
				
				checkAdd( instance );
				
			}else if ( type == MT_BYE ){
				
				checkRemove( instance );
				
			}else{
				
				checkAdd( instance );
				
				Map	body = (Map)map.get( "body" );
				
				if ( type == MT_REQUEST ){
					
					String	originator_id	= instance.getID();
					
					if ( !originator_id.equals( my_instance.getID())){
						
						Map	reply = requestReceived( instance, body );
					
						if ( reply != null ){
						
							reply.put( "oid", originator_id.getBytes());
							reply.put( "rid", body.get( "rid" ));
							
							sendMessage( MT_REPLY, reply, originator );
						}
					}
				}else if ( 	type == MT_REPLY ){
					
					String	originator_id	= new String((byte[])body.get( "oid" ));
					
					if ( originator_id.equals( my_instance.getID())){
						
						long req_id = ((Long)body.get("rid")).longValue();
						
						try{
							this_mon.enter();
							
							for (int i=0;i<requests.size();i++){
								
								request	req = (request)requests.get(i);
								
								if ( req.getID() == req_id ){
									
									req.addReply( instance, body );
								}
							}
						}finally{
							
							this_mon.exit();
						}
					}
				}
			}
		}catch( Throwable e ){
			
			Debug.out( "Invalid packet received from " + originator, e );
		}
	
protected voidremoveAddresses(AZOtherInstanceImpl inst)

		List		internal_addresses 	= inst.getInternalAddresses();
		InetAddress	external_address	= inst.getExternalAddress();
		int			tcp					= inst.getTCPListenPort();
		int			udp					= inst.getUDPListenPort();
		int			udp2				= inst.getUDPNonDataListenPort();

		for (int i=0;i<internal_addresses.size();i++){
			
			modifyAddresses( (InetAddress)internal_addresses.get(i), external_address, tcp, udp, udp2, false );
		}
	
public voidremoveListener(com.aelitis.azureus.core.instancemanager.AZInstanceManagerListener l)

		listeners.remove( l );
	
protected java.util.MaprequestReceived(com.aelitis.azureus.core.instancemanager.AZInstance instance, java.util.Map body)

		// System.out.println( "received result: " + ST + "/" + AL );
		

		long	type = ((Long)body.get( "type")).longValue();
		
		if ( type == MT_REQUEST_SEARCH ){
			
			return( new HashMap());
			
		}else if ( type == MT_REQUEST_TRACK ){
							
			byte[]	hash = (byte[])body.get( "hash" );
			
			boolean	seed = ((Long)body.get( "seed" )).intValue() == 1;
			
			List	dms = core.getGlobalManager().getDownloadManagers();
			
			Iterator	it = dms.iterator();
			
			DownloadManager	matching_dm = null;
			
			try{
				while( it.hasNext()){
					
					DownloadManager	dm = (DownloadManager)it.next();
					
					TOTorrent	torrent = dm.getTorrent();
					
					if ( torrent == null ){
						
						continue;
					}
					
					byte[]	sha1_hash = (byte[])dm.getData( "AZInstanceManager::sha1_hash" );
					
					if ( sha1_hash == null ){			

						sha1_hash	= new SHA1Simple().calculateHash( torrent.getHash());
						
						dm.setData( "AZInstanceManager::sha1_hash", sha1_hash );
					}
					
					if ( Arrays.equals( hash, sha1_hash )){
						
						matching_dm	= dm;
						
						break;
					}
				}
			}catch( Throwable e ){
				
				Debug.printStackTrace(e);
			}
			
			if ( matching_dm == null ){
				
				return( null );
			}
			
			int	dm_state = matching_dm.getState();
			
			if ( dm_state == DownloadManager.STATE_ERROR || dm_state == DownloadManager.STATE_STOPPED ){
				
				return( null );
			}
							
			try{		
				informTracked( 
					new trackedInstance( instance, DownloadManagerImpl.getDownloadStatic( matching_dm ), seed ));
					
			}catch( Throwable e ){
					
				Debug.printStackTrace(e);
			}
			
			Map	reply = new HashMap();
			
			// XXX include DND?  I don't know
			reply.put( "seed", new Long( matching_dm.isDownloadComplete(true)?1:0));		
			
			return( reply );
			
		}else{
			
			return( null );
		}
	
protected voidsearch()

		sendRequest( MT_REQUEST_SEARCH );
	
protected voidsendAlive()

		sendMessage( MT_ALIVE );
	
protected voidsendAlive(java.net.InetSocketAddress target)

		sendMessage( MT_ALIVE, target );
	
protected voidsendByeBye()

		sendMessage( MT_BYE );
	
protected voidsendByeBye(java.net.InetSocketAddress target)

		sendMessage( MT_BYE, target );
	
protected voidsendMessage(int type)

		sendMessage( type, (Map)null );
	
protected voidsendMessage(int type, java.net.InetSocketAddress target)

		sendMessage( type, null, target );
	
protected voidsendMessage(int type, java.util.Map body)

		sendMessage( type, body, null );
	
protected voidsendMessage(int type, java.util.Map body, java.net.InetSocketAddress member)

		Map	map = new HashMap();
		
		map.put( "ver", new Long(MT_VERSION));
		map.put( "type", new Long(type));
		
		Map	originator = new HashMap();
		
		map.put( "orig", originator );
		
		my_instance.encode( originator );
		
		if ( body != null ){
			
			map.put( "body", body );
		}
				
		try{
			
			if ( member == null ){
				
				byte[]	data = BEncoder.encode( map );

				mc_group.sendToGroup( data );
				
				if ( explicit_peers.size() > 0 ){
					
					map.put( "explicit", new Long(1));
				
					byte[]	explicit_data = BEncoder.encode( map );
					
					Iterator	it = explicit_peers.iterator();
				
					while( it.hasNext()){
					
						mc_group.sendToMember((InetSocketAddress)it.next(), explicit_data );
					}
				}
			}else{
				
				if ( explicit_peers.contains( member )){
					
					map.put( "explicit", new Long(1));
				}
				
				byte[]	explicit_data = BEncoder.encode( map );

				mc_group.sendToMember( member, explicit_data );
			}
		}catch( Throwable e ){
			
		}
	
protected java.util.MapsendRequest(int type)

		return( new request( type, new HashMap()).getReplies());
	
protected java.util.MapsendRequest(int type, java.util.Map body)

		return( new request( type, body ).getReplies());
	
public voidsetIncludeWellKnownLANs(boolean include)

		include_well_known_lans	= include;
	
public voidtrace(java.lang.String str)

		if ( Logger.isEnabled()){
				
			Logger.log(new LogEvent( LOGID, str )); 
		}
	
public com.aelitis.azureus.core.instancemanager.AZInstanceTracked[]track(org.gudy.azureus2.plugins.download.Download download)

		if ( mc_group == null || download.getTorrent() == null || getOtherInstances().length == 0 ){
			
			return( new AZInstanceTracked[0]);
		}
		
		Map	body = new HashMap();
		
		body.put( "hash", new SHA1Simple().calculateHash(download.getTorrent().getHash()));
		
		body.put( "seed", new Long( download.isComplete()?1:0 ));
		
		Map	replies = sendRequest( MT_REQUEST_TRACK, body ); 
				
		AZInstanceTracked[]	res = new AZInstanceTracked[replies.size()];
		
		Iterator	it = replies.entrySet().iterator();
		
		int	pos = 0;
		
		while( it.hasNext()){
			
			Map.Entry	entry = (Map.Entry)it.next();
			
			AZInstance	inst 	= (AZInstance)entry.getKey();
			Map			reply	= (Map)entry.getValue();
	
			boolean	seed = ((Long)reply.get( "seed" )).intValue() == 1;
	
			res[ pos++ ] = new trackedInstance( inst, download, seed );
		}
		
		return( res );