1 /*
2  * Created on Jul 16, 2008
3  * Created by Paul Gardner
4  *
5  * Copyright (C) Azureus Software, Inc, All Rights Reserved.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  */
19 
20 
21 package com.aelitis.azureus.core.lws;
22 
23 import java.io.File;
24 import java.net.InetSocketAddress;
25 import java.net.URL;
26 
27 import org.gudy.azureus2.core3.disk.DiskManager;
28 import org.gudy.azureus2.core3.disk.DiskManagerReadRequest;
29 import org.gudy.azureus2.core3.disk.DiskManagerReadRequestListener;
30 import org.gudy.azureus2.core3.logging.LogEvent;
31 import org.gudy.azureus2.core3.logging.LogIDs;
32 import org.gudy.azureus2.core3.logging.LogRelation;
33 import org.gudy.azureus2.core3.logging.Logger;
34 import org.gudy.azureus2.core3.peer.PEPeer;
35 import org.gudy.azureus2.core3.peer.PEPeerManager;
36 import org.gudy.azureus2.core3.peer.PEPeerManagerFactory;
37 import org.gudy.azureus2.core3.peer.PEPeerManagerListenerAdapter;
38 import org.gudy.azureus2.core3.peer.util.PeerUtils;
39 import org.gudy.azureus2.core3.torrent.TOTorrent;
40 import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncer;
41 import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncerDataProvider;
42 import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncerException;
43 import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncerFactory;
44 import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncerListener;
45 import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncerResponse;
46 import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncerResponsePeer;
47 import org.gudy.azureus2.core3.util.AddressUtils;
48 import org.gudy.azureus2.core3.util.ByteFormatter;
49 import org.gudy.azureus2.core3.util.HashWrapper;
50 import org.gudy.azureus2.core3.util.SystemTime;
51 import org.gudy.azureus2.plugins.torrent.Torrent;
52 import org.gudy.azureus2.pluginsimpl.local.torrent.TorrentImpl;
53 
54 import com.aelitis.azureus.core.networkmanager.NetworkConnection;
55 import com.aelitis.azureus.core.networkmanager.NetworkManager;
56 import com.aelitis.azureus.core.peermanager.PeerManager;
57 import com.aelitis.azureus.core.peermanager.PeerManagerRegistration;
58 import com.aelitis.azureus.core.peermanager.PeerManagerRegistrationAdapter;
59 
60 
61 
62 public class
63 LightWeightSeed
64 	extends LogRelation
65 	implements PeerManagerRegistrationAdapter
66 {
67 	private static final byte	ACT_NONE					= 0;
68 	private static final byte	ACT_HAS_PEERS				= 2;
69 	private static final byte	ACT_HAS_POTENTIAL_PEERS		= 3;
70 	private static final byte	ACT_INCOMING				= 4;
71 	private static final byte	ACT_NO_PM					= 5;
72 	private static final byte	ACT_TIMING_OUT				= 6;
73 	private static final byte	ACT_TRACKER_ANNOUNCE		= 7;
74 	private static final byte	ACT_TRACKER_SCRAPE			= 8;
75 
76 	private static final int DEACTIVATION_TIMEOUT					= 5*60*1000;
77 	private static final int DEACTIVATION_WITH_POTENTIAL_TIMEOUT	= 15*60*1000;
78 
79 	final private LightWeightSeedManager		manager;
80 	final private LightWeightSeedAdapter		adapter;
81 	final private String						name;
82 	final private HashWrapper					hash;
83 	final private URL							announce_url;
84 	final private File							data_location;
85 	final private String						network;
86 
87 	private PeerManagerRegistration		peer_manager_registration;
88 
89 	private volatile PEPeerManager		peer_manager;
90 	private volatile LWSDiskManager		disk_manager;
91 	private LWSDownload					pseudo_download;
92 	private volatile LWSTorrent			torrent_facade;
93 	private TRTrackerAnnouncer			announcer;
94 
95 	private TOTorrent					actual_torrent;
96 
97 	private boolean		is_running;
98 	private long		last_activity_time;
99 	private int			activation_state		= ACT_NONE;
100 
101 	protected
LightWeightSeed( LightWeightSeedManager _manager, String _name, HashWrapper _hash, URL _announce_url, File _data_location, String _network, LightWeightSeedAdapter _adapter )102 	LightWeightSeed(
103 		LightWeightSeedManager	_manager,
104 		String					_name,
105 		HashWrapper				_hash,
106 		URL						_announce_url,
107 		File					_data_location,
108 		String					_network,
109 		LightWeightSeedAdapter	_adapter )
110 	{
111 		manager			= _manager;
112 		name			= _name;
113 		hash			= _hash;
114 		announce_url	= _announce_url;
115 		data_location	= _data_location;
116 		network			= _network;
117 		adapter			= _adapter;
118 	}
119 
120 	protected String
getName()121 	getName()
122 	{
123 		return( name + "/" + ByteFormatter.encodeString( hash.getBytes()));
124 	}
125 
126 	protected Torrent
getTorrent()127 	getTorrent()
128 	{
129 		return( new TorrentImpl( getTOTorrent( false )));
130 	}
131 
132 	protected TOTorrent
getTOTorrent( boolean actual )133 	getTOTorrent(
134 		boolean		actual )
135 	{
136 		if ( actual ){
137 
138 			synchronized( this ){
139 
140 				if ( actual_torrent == null ){
141 
142 					try{
143 
144 						actual_torrent = adapter.getTorrent( hash.getBytes(), announce_url, data_location );
145 
146 					}catch( Throwable e ){
147 
148 						log( "Failed to get torrent", e );
149 					}
150 
151 					if ( actual_torrent == null ){
152 
153 						throw( new RuntimeException( "Torrent not available" ));
154 					}
155 				}
156 
157 				return( actual_torrent );
158 			}
159 		}else{
160 
161 			return( torrent_facade );
162 		}
163 	}
164 
165 	public HashWrapper
getHash()166 	getHash()
167 	{
168 		return( hash );
169 	}
170 
171 	public URL
getAnnounceURL()172 	getAnnounceURL()
173 	{
174 		return( announce_url );
175 	}
176 
177 	public File
getDataLocation()178 	getDataLocation()
179 	{
180 		return( data_location );
181 	}
182 
183 	public String
getNetwork()184 	getNetwork()
185 	{
186 		return( network );
187 	}
188 
189 	protected long
getSize()190 	getSize()
191 	{
192 		return( data_location.length());
193 	}
194 
195 	public boolean
isPeerSourceEnabled( String peer_source )196 	isPeerSourceEnabled(
197 		String		peer_source )
198 	{
199 		return( true );
200 	}
201 
202 	public boolean
manualRoute( NetworkConnection connection )203 	manualRoute(
204 		NetworkConnection connection )
205 	{
206 		return false;
207 	}
208 
209 	public byte[][]
getSecrets()210 	getSecrets()
211 	{
212 		return( new byte[][]{ hash.getBytes()});
213 	}
214 
215 	public boolean
activateRequest( InetSocketAddress remote_address )216 	activateRequest(
217 		InetSocketAddress		remote_address )
218 	{
219 		ensureActive( "Incoming[" + AddressUtils.getHostAddress( remote_address ) + "]", ACT_INCOMING );
220 
221 		return( true );
222 	}
223 
224 	public void
deactivateRequest( InetSocketAddress remote_address )225 	deactivateRequest(
226 		InetSocketAddress		remote_address )
227 	{
228 
229 	}
230 
231 	public String
getDescription()232 	getDescription()
233 	{
234 		return( "LWS: " + getName());
235 	}
236 
237 	protected synchronized void
start()238 	start()
239 	{
240 		log( "Start" );
241 
242 		if ( is_running ){
243 
244 			log( "Start of '" + getString() + "' failed - already running" );
245 
246 			return;
247 		}
248 
249 		if ( peer_manager_registration != null ){
250 
251 			log( "Start of '" + getString() + "' failed - router already registered" );
252 
253 			return;
254 		}
255 
256 		if ( pseudo_download != null ){
257 
258 			log( "Start of '" + getString() + "' failed - pseudo download already registered" );
259 
260 			return;
261 		}
262 
263 		if ( disk_manager != null ){
264 
265 			log( "Start of '" + getString() + "' failed - disk manager already started" );
266 
267 			return;
268 		}
269 
270 		if ( peer_manager != null ){
271 
272 			log( "Start of '" + getString() + "' failed - peer manager already started" );
273 
274 			return;
275 		}
276 
277 		try{
278 			if ( torrent_facade == null ){
279 
280 				torrent_facade = new LWSTorrent( this );
281 			}
282 
283 			peer_manager_registration = PeerManager.getSingleton().registerLegacyManager( hash, this );
284 
285 			announcer = createAnnouncer();
286 
287 			pseudo_download = new LWSDownload( this, announcer );
288 
289 			manager.addToDHTTracker( pseudo_download );
290 
291 			is_running	= true;
292 
293 			last_activity_time = SystemTime.getMonotonousTime();
294 
295 		}catch( Throwable e ){
296 
297 			log( "Start of '" + getString() + "' failed", e );
298 
299 		}finally{
300 
301 			if ( is_running ){
302 
303 				log( "Started " + getString());
304 
305 			}else{
306 
307 				stop();
308 			}
309 		}
310 	}
311 
312 	protected synchronized void
stop()313 	stop()
314 	{
315 		log( "Stop" );
316 
317 		try{
318 			if ( disk_manager != null ){
319 
320 				disk_manager.stop( false );
321 
322 				disk_manager	= null;
323 			}
324 
325 			if ( peer_manager != null ){
326 
327 				peer_manager.stopAll();
328 
329 				peer_manager = null;
330 			}
331 
332 			if ( pseudo_download != null ){
333 
334 				manager.removeFromDHTTracker( pseudo_download );
335 
336 				pseudo_download = null;
337 			}
338 
339 			if ( announcer != null ){
340 
341 				announcer.stop( false );
342 
343 				announcer.destroy();
344 
345 				announcer = null;
346 			}
347 
348 			if ( peer_manager_registration != null ){
349 
350 				peer_manager_registration.unregister();
351 
352 				peer_manager_registration	= null;
353 			}
354 
355 		}finally{
356 
357 			is_running 	= false;
358 
359 			activation_state 	= ACT_NONE;
360 
361 			log( "Stopped " + getString());
362 		}
363 	}
364 
365 	protected synchronized void
activate( String reason_str, byte activation_reason )366 	activate(
367 		String		reason_str,
368 		byte		activation_reason )
369 	{
370 		log( "Activate: " + activation_reason + "/" + reason_str );
371 
372 		if ( activation_state != ACT_NONE ){
373 
374 			return;
375 		}
376 
377 		try{
378 			disk_manager = new LWSDiskManager( this, data_location );
379 
380 			disk_manager.start();
381 
382 			if ( disk_manager.getState() != DiskManager.READY ){
383 
384 				log( "Start of '" + getString() + "' failed, disk manager failed = " + disk_manager.getErrorMessage());
385 
386 			}else{
387 
388 				peer_manager =
389 					PEPeerManagerFactory.create(
390 								announcer.getPeerId(),
391 								new LWSPeerManagerAdapter(
392 										this,
393 										peer_manager_registration ),
394 								disk_manager );
395 
396 				peer_manager.addListener(
397 					new PEPeerManagerListenerAdapter()
398 					{
399 						public void
400 						peerAdded(
401 							final PEPeerManager 	manager,
402 							final PEPeer 			peer )
403 						{
404 							last_activity_time = SystemTime.getMonotonousTime();
405 						}
406 
407 
408 						public void
409 						peerRemoved(
410 							PEPeerManager 	manager,
411 							PEPeer 			peer )
412 						{
413 							last_activity_time = SystemTime.getMonotonousTime();
414 						}
415 					});
416 
417 				peer_manager.start();
418 
419 				announcer.update( true );
420 
421 				activation_state	= activation_reason;
422 
423 				last_activity_time = SystemTime.getMonotonousTime();
424 			}
425 
426 		}catch( Throwable e ){
427 
428 			log( "Activation of '" + getString() + "' failed", e );
429 
430 		}finally{
431 
432 			if ( activation_state != ACT_NONE ){
433 
434 
435 			}else{
436 
437 				deactivate();
438 			}
439 		}
440 	}
441 
442 	protected synchronized void
deactivate()443 	deactivate()
444 	{
445 		log( "Deactivate" );
446 
447 		try{
448 			if ( disk_manager != null ){
449 
450 				disk_manager.stop( false );
451 
452 				disk_manager	= null;
453 			}
454 
455 			if ( peer_manager != null ){
456 
457 				peer_manager.stopAll();
458 
459 				peer_manager = null;
460 			}
461 		}finally{
462 
463 			activation_state = ACT_NONE;
464 		}
465 	}
466 
467 	protected synchronized TRTrackerAnnouncer
createAnnouncer()468 	createAnnouncer()
469 		throws TRTrackerAnnouncerException
470 	{
471 
472 			// use a facade here to delay loading the actual torrent until the
473 			// download is activated
474 
475 		TRTrackerAnnouncer result = TRTrackerAnnouncerFactory.create( torrent_facade, true );
476 
477 		result.addListener(
478 				new TRTrackerAnnouncerListener()
479 				{
480 					public void
481 					receivedTrackerResponse(
482 						TRTrackerAnnouncerResponse	response )
483 					{
484 						TRTrackerAnnouncerResponsePeer[] peers = response.getPeers();
485 
486 							// tracker shouldn't return seeds to seeds to we can assume
487 							// that if peers returned this means we have someone to talk to
488 
489 						if ( peers != null && peers.length > 0 ){
490 
491 							ensureActive( "Tracker[" + peers[0].getAddress()+ "]", ACT_TRACKER_ANNOUNCE );
492 
493 						}else if ( response.getScrapeIncompleteCount() > 0 ){
494 
495 							ensureActive( "Tracker[scrape]", ACT_TRACKER_SCRAPE );
496 						}
497 
498 						PEPeerManager	pm = peer_manager;
499 
500 						if ( pm != null ){
501 
502 							pm.processTrackerResponse( response );
503 						}
504 					}
505 
506 					public void
507 					urlChanged(
508 						TRTrackerAnnouncer	announcer,
509 						URL					old_url,
510 						URL					new_url,
511 						boolean				explicit )
512 					{
513 					}
514 
515 					public void
516 					urlRefresh()
517 					{
518 					}
519 				});
520 
521 		result.setAnnounceDataProvider(
522 				new TRTrackerAnnouncerDataProvider()
523 				{
524 					public String
525 					getName()
526 					{
527 						return( LightWeightSeed.this.getName());
528 					}
529 
530 					public long
531 					getTotalSent()
532 					{
533 						return( 0 );
534 					}
535 
536 					public long
537 					getTotalReceived()
538 					{
539 						return( 0 );
540 					}
541 
542 	    			public long
543 	    			getFailedHashCheck()
544 	    			{
545 	    				return( 0 );
546 	    			}
547 
548 					public long
549 					getRemaining()
550 					{
551 						return( 0 );
552 					}
553 
554 					public String
555 					getExtensions()
556 					{
557 						return( null );
558 					}
559 
560 					public int
561 					getMaxNewConnectionsAllowed(
562 						String	network )
563 					{
564 						PEPeerManager	pm = peer_manager;
565 
566 						if ( pm == null ){
567 
568 								// got to ask for at least one to trigger activation!
569 
570 							return( 8 );
571 						}
572 
573 						return( PeerUtils.numNewConnectionsAllowed( pm.getPeerIdentityDataID(),0));
574 					}
575 
576 					public int
577 					getPendingConnectionCount()
578 					{
579 						PEPeerManager	pm = peer_manager;
580 
581 						if ( pm == null ){
582 
583 							return( 0 );
584 						}
585 
586 						return( pm.getPendingPeerCount());
587 					}
588 
589 					public int
590 					getConnectedConnectionCount()
591 					{
592 						PEPeerManager	pm = peer_manager;
593 
594 						if ( pm == null ){
595 
596 							return( 0 );
597 						}
598 
599 						return( pm.getNbPeers() + pm.getNbSeeds());
600 					}
601 
602 					public int
603 					getUploadSpeedKBSec(
604 						boolean estimate)
605 					{
606 						return 0;
607 					}
608 
609 					public int
610 					getCryptoLevel()
611 					{
612 						return( NetworkManager.CRYPTO_OVERRIDE_NONE );
613 					}
614 
615 					public boolean
616 					isPeerSourceEnabled(
617 						String		peer_source )
618 					{
619 						return( true );
620 					}
621 
622 					public void
623 					setPeerSources(
624 						String[]	sources )
625 					{
626 					}
627 				});
628 
629 		return( result );
630 	}
631 
632 	protected synchronized void
ensureActive( String reason, byte a_reason )633 	ensureActive(
634 		String	reason,
635 		byte	a_reason )
636 	{
637 		if ( is_running && activation_state == ACT_NONE ){
638 
639 			activate( reason, a_reason );
640 		}
641 	}
642 
643 	protected synchronized void
checkDeactivation()644 	checkDeactivation()
645 	{
646 		if ( activation_state == ACT_NONE ){
647 
648 			return;
649 		}
650 
651 		if ( peer_manager == null ){
652 
653 			activation_state	= ACT_NO_PM;
654 
655 			return;
656 		}
657 
658 
659 		if ( peer_manager.getNbPeers() > 0 ){
660 
661 			activation_state	= ACT_HAS_PEERS;
662 
663 			return;
664 		}
665 
666 		long	now = SystemTime.getMonotonousTime();
667 
668 		long	millis_since_last_act = now - last_activity_time;
669 
670 		if ( peer_manager.hasPotentialConnections()){
671 
672 			if ( millis_since_last_act < DEACTIVATION_WITH_POTENTIAL_TIMEOUT ){
673 
674 				activation_state	= ACT_HAS_POTENTIAL_PEERS;
675 
676 				return;
677 			}
678 		}
679 
680 		if ( millis_since_last_act >= DEACTIVATION_TIMEOUT ){
681 
682 			deactivate();
683 
684 		}else{
685 
686 			activation_state	= ACT_TIMING_OUT;
687 		}
688 	}
689 
690 	public void
enqueueReadRequest( PEPeer peer, DiskManagerReadRequest request, DiskManagerReadRequestListener listener )691 	enqueueReadRequest(
692 		PEPeer							peer,
693 		DiskManagerReadRequest 			request,
694 		DiskManagerReadRequestListener 	listener )
695 	{
696 		LWSDiskManager	dm = disk_manager;
697 
698 		if ( dm == null ){
699 
700 			listener.readFailed( request, new Throwable( "download is stopped" ));
701 
702 		}else{
703 
704 			dm.enqueueReadRequest( request, listener );
705 		}
706 	}
707 
708 	public void
remove()709 	remove()
710 	{
711 		manager.remove( this );
712 	}
713 
714 	public String
getRelationText()715 	getRelationText()
716 	{
717 		return "LWS: '" + getName() + "'";
718 	}
719 
720 	public Object[]
getQueryableInterfaces()721 	getQueryableInterfaces()
722 	{
723 		return new Object[]{};
724 	}
725 
726 	public LogRelation
getRelation()727 	getRelation()
728 	{
729 		return( this );
730 	}
731 
732 	protected String
getString()733 	getString()
734 	{
735 		return( getName());
736 	}
737 
738 	protected void
log( String str )739 	log(
740 		String		str )
741 	{
742 		Logger.log(new LogEvent(this, LogIDs.CORE, str ));
743 	}
744 
745 	protected void
log( String str, Throwable e )746 	log(
747 		String		str,
748 		Throwable	e )
749 	{
750 		Logger.log(new LogEvent(this, LogIDs.CORE, str, e ));
751 	}
752 }
753