/*
* JMule - Java file sharing client
* Copyright (C) 2007-2008 JMule team ( jmule@jmule.org / http://jmule.org )
*
* Any parts of this program derived from other projects, or contributed
* by third-party developers are copyrighted by their respective authors.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package org.jmule.core.peermanager;
import static org.jmule.core.JMConstants.KEY_SEPARATOR;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.jmule.core.JMuleAbstractManager;
import org.jmule.core.JMuleManagerException;
import org.jmule.core.downloadmanager.DownloadManagerSingleton;
import org.jmule.core.downloadmanager.InternalDownloadManager;
import org.jmule.core.edonkey.ClientID;
import org.jmule.core.edonkey.E2DKConstants;
import org.jmule.core.edonkey.UserHash;
import org.jmule.core.edonkey.E2DKConstants.PeerFeatures;
import org.jmule.core.edonkey.packet.tag.Tag;
import org.jmule.core.edonkey.packet.tag.TagList;
import org.jmule.core.edonkey.utils.Utils;
import org.jmule.core.networkmanager.InternalNetworkManager;
import org.jmule.core.networkmanager.NetworkManagerException;
import org.jmule.core.networkmanager.NetworkManagerSingleton;
import org.jmule.core.peermanager.Peer.PeerSource;
import org.jmule.core.peermanager.Peer.PeerStatus;
import org.jmule.core.statistics.JMuleCoreStats;
import org.jmule.core.statistics.JMuleCoreStatsProvider;
import org.jmule.core.uploadmanager.InternalUploadManager;
import org.jmule.core.uploadmanager.UploadManagerSingleton;
import org.jmule.core.utils.timer.JMTimer;
import org.jmule.core.utils.timer.JMTimerTask;
/**
*
* @author binary256
* @author javajox
* @version $$Revision: 1.23 $$
* Last changed by $$Author: binary255 $$ on $$Date: 2010/01/12 14:41:01 $$
*/
public class PeerManagerImpl extends JMuleAbstractManager implements InternalPeerManager {
private Map<String, Peer> peers = new ConcurrentHashMap<String, Peer>();
private InternalNetworkManager _network_manager;
private List<PeerManagerListener> listener_list = new LinkedList<PeerManagerListener>();
private InternalDownloadManager _download_manager = (InternalDownloadManager) DownloadManagerSingleton.getInstance();
private InternalUploadManager _upload_manager = (InternalUploadManager) UploadManagerSingleton.getInstance();
private JMTimer maintenance_tasks = new JMTimer();
PeerManagerImpl() {
}
public String toString() {
String result = "";
for(String key : peers.keySet()) {
result += "[" + key +"]= " + "["+peers.get(key)+"]\n";
}
return result;
}
public void initialize() {
try {
super.initialize();
} catch (JMuleManagerException e) {
e.printStackTrace();
return;
}
Set<String> types = new HashSet<String>();
types.add(JMuleCoreStats.ST_NET_PEERS_COUNT);
JMuleCoreStats.registerProvider(types, new JMuleCoreStatsProvider() {
public void updateStats(Set<String> types, Map<String, Object> values) {
if (types.contains(JMuleCoreStats.ST_NET_PEERS_COUNT)) {
values.put(JMuleCoreStats.ST_NET_PEERS_COUNT, peers.size());
}
}
});
_network_manager = (InternalNetworkManager) NetworkManagerSingleton
.getInstance();
}
public void shutdown() {
try {
super.shutdown();
} catch (JMuleManagerException e) {
e.printStackTrace();
return ;
}
maintenance_tasks.cancelAllTasks();
for(Peer peer : peers.values()) {
try {
if (peer.isConnected())
disconnect(peer);
}catch(PeerManagerException e) {
e.printStackTrace();
}
}
}
public void start() {
try {
super.start();
} catch (JMuleManagerException e) {
e.printStackTrace();
return ;
}
JMTimerTask peer_dropper = new JMTimerTask() {
public void run() {
for(Peer peer : peers.values()) {
if (!peer.isConnected()) continue;
if (!_download_manager.hasPeer(peer))
if (!_upload_manager.hasPeer(peer)) {
try {
disconnect(peer);
} catch (PeerManagerException e) {
e.printStackTrace();
}
}
}
}
};
maintenance_tasks.addTask(peer_dropper, 10000, true);
}
protected boolean iAmStoppable() {
return false;
}
public List<Peer> getPeers() {
List<Peer> list = new ArrayList<Peer>();
list.addAll(peers.values());
return list;
}
public Peer getPeer(String ip, int port) throws PeerManagerException {
Peer peer = peers.get(ip + KEY_SEPARATOR + port);
if (peer == null)
throw new PeerManagerException("Peer " + ip + KEY_SEPARATOR + port + " not found");
return peer;
}
public Peer newPeer(String ip, int port, PeerSource source) throws PeerManagerException {
if (hasPeer(ip, port))
throw new PeerManagerException("Peer already exists");
Peer peer = new Peer(ip, port, source);
peers.put(ip + KEY_SEPARATOR + port, peer);
peer.setStatus(PeerStatus.DISCONNECTED);
notifyNewPeer(peer);
return peer;
}
public void removePeer(Peer peer) throws PeerManagerException {
String ip = peer.getIP();
int port = peer.getPort();
if (! hasPeer(ip, port))
throw new PeerManagerException(" Peer " + ip + " : " + port + " not found");
if (peer.getStatus()!= PeerStatus.DISCONNECTED)
_network_manager.disconnectPeer(ip, port);
peers.remove(ip + KEY_SEPARATOR + port);
notifyPeerRemoved(peer);
}
public Peer newIncomingPeer(String ip, int port) throws PeerManagerException {
Peer peer;
if (hasPeer(ip, port)) {
peer = getPeer(ip, port);
}
peer = new Peer(ip, port, PeerSource.SERVER);
peers.put(ip + KEY_SEPARATOR + port, peer);
notifyNewPeer(peer);
return peer;
}
/**
* Search replication peer and replace it
* @param peer
* @return
*/
private boolean replaceLowIDPeer(Peer peer) {
Peer founded_peer = null;
for(Peer stored_peer : peers.values()) {
if (stored_peer.getID().equals(peer.getID()))
if (!stored_peer.isConnected()) {
founded_peer = stored_peer;
break;
}
}
//System.out.println("Founded low ID peer : " + founded_peer +"\n for peer : " + peer);
if (founded_peer != null) {
String founded_peer_key = founded_peer.getIP() + KEY_SEPARATOR + founded_peer.getPort();
//System.out.println("replaceLowIDPeer");
//System.out.println("Search key : " + founded_peer_key);
//System.out.println("Replace : " + peer + "\n"+founded_peer);
founded_peer.copyFields(peer);
String remove_peer_key = peer.getIP() + KEY_SEPARATOR + peer.getPort();
peers.remove(remove_peer_key);
peers.remove(founded_peer_key);
peers.put(remove_peer_key, founded_peer);
return true;
}
return false;
}
public void helloAnswerFromPeer(String peerIP, int peerPort,
UserHash userHash, ClientID clientID, int peerListenPort,
TagList tagList, String serverIP, int serverPort) {
Peer peer = null;
try {
peer = getPeer(peerIP, peerPort);
} catch (PeerManagerException e) {
e.printStackTrace();
return ;
}
peers.remove(peerIP + KEY_SEPARATOR + peerPort);
peers.put(peerIP + KEY_SEPARATOR + peerListenPort, peer);
peer.setListenPort(peerListenPort);
peer.setStatus(PeerStatus.CONNECTED);
peer.setUserHash(userHash);
peer.setClientID(clientID);
peer.setTagList(tagList);
peer.setServer(serverIP, serverPort);
if (!peer.isHighID())
if (replaceLowIDPeer(peer)) {
try {
peer = getPeer(peerIP, peerListenPort);
peer.setStatus(PeerStatus.CONNECTED);
} catch (PeerManagerException e) {
e.printStackTrace();
return ;
}
}
_download_manager.peerConnected(peer);
_upload_manager.peerConnected(peer);
notifyPeerConnected(peer);
}
public void helloFromPeer(String peerIP, int peerPort, UserHash userHash,
ClientID clientID, int peerListenPort, TagList tagList,
String serverIP, int serverPort) {
Peer peer = null;
try {
peer = getPeer(peerIP, peerPort);
} catch (PeerManagerException e) {
e.printStackTrace();
return ;
}
peers.remove(peerIP + KEY_SEPARATOR + peerPort);
peers.put(peerIP + KEY_SEPARATOR + peerListenPort, peer);
peer.setListenPort(peerListenPort);
peer.setStatus(PeerStatus.CONNECTED);
peer.setUserHash(userHash);
peer.setClientID(clientID);
peer.setTagList(tagList);
peer.setServer(serverIP, serverPort);
if (!peer.isHighID())
if (replaceLowIDPeer(peer)) {
try {
peer = getPeer(peerIP, peerListenPort);
peer.setStatus(PeerStatus.CONNECTED);
} catch (PeerManagerException e) {
e.printStackTrace();
return ;
}
}
_download_manager.peerConnected(peer);
_upload_manager.peerConnected(peer);
notifyPeerConnected(peer);
}
public void connect(Peer peer) throws PeerManagerException {
String ip = peer.getIP();
int port = peer.getPort();
if (!hasPeer(ip, port))
throw new PeerManagerException("Peer " + ip + KEY_SEPARATOR + port + " not found");
try {
_network_manager.addPeer(ip, peer.getListenPort());
}catch(NetworkManagerException cause) {
throw new PeerManagerException(cause);
}
peer.setStatus(PeerStatus.CONNECTING);
notifyPeerConnecting(peer);
}
public void disconnect(Peer peer) throws PeerManagerException {
String ip = peer.getIP();
int port = peer.getPort();
if (!hasPeer(ip, port))
throw new PeerManagerException("Peer " + ip + KEY_SEPARATOR + port + " not found");
_network_manager.disconnectPeer(ip, port);
peer.setStatus(PeerStatus.DISCONNECTED);
notifyPeerDisconnected(peer);
}
public void peerConnectingFailed(String ip, int port, Throwable cause) {
Peer peer = null;
try {
peer = getPeer(ip, port);
} catch (PeerManagerException e) {
e.printStackTrace();
return ;
}
peer.setStatus(PeerStatus.DISCONNECTED);
_download_manager.peerConnectingFailed(peer, cause);
_upload_manager.peerConnectingFailed(peer, cause);
notifyPeerConnectingFailed(peer, cause);
}
public void peerDisconnected(String ip, int port) {
Peer peer;
try {
peer = getPeer(ip, port);
} catch (PeerManagerException e) {
e.printStackTrace();
return;
}
peer.setStatus(PeerStatus.DISCONNECTED);
_download_manager.peerDisconnected(peer);
_upload_manager.peerDisconnected(peer);
notifyPeerDisconnected(peer);
}
public boolean hasPeer(String ip, int port) {
return peers.containsKey(ip + KEY_SEPARATOR + port);
}
public void callBackRequestFailed() {
}
public void receivedCallBackRequest(String ip, int port) {
try {
Peer peer = newPeer(ip, port, PeerSource.SERVER);
connect(peer);
} catch (PeerManagerException e) {
e.printStackTrace();
}
}
public void receivedEMuleHelloFromPeer(String ip, int port,
byte clientVersion, byte protocolVersion, TagList tagList) {
try {
Peer peer = getPeer(ip, port);
Map<PeerFeatures,Integer> peer_features = Utils.scanTagListPeerFeatures(tagList);
peer_features.put(PeerFeatures.ProtocolVersion, (int)protocolVersion);
peer.peer_features.putAll(peer_features);
} catch (PeerManagerException e) {
e.printStackTrace();
}
}
public void receivedEMuleHelloAnswerFromPeer(String ip, int port,
byte clientVersion, byte protocolVersion, TagList tagList) {
try {
Peer peer = getPeer(ip, port);
Map<PeerFeatures,Integer> peer_features = Utils.scanTagListPeerFeatures(tagList);
peer_features.put(PeerFeatures.ProtocolVersion, (int)protocolVersion);
peer.peer_features.putAll(peer_features);
Tag udp_port = tagList.getTag(E2DKConstants.ET_UDPPORT);
if (udp_port != null)
peer.tag_list.addTag(udp_port);
} catch (PeerManagerException e) {
e.printStackTrace();
}
}
public void addPeerManagerListener(PeerManagerListener listener) {
listener_list.add(listener);
}
public void removePeerManagerListener(PeerManagerListener listener) {
listener_list.remove(listener);
}
public List<Peer> createPeerList(List<String> peerIPList, List<Integer> peerPort, boolean addKnownPeersInList, PeerSource peerSource) {
List<Peer> result = new ArrayList<Peer>();
for (int i = 0; i < peerIPList.size(); i++) {
String peer_ip = peerIPList.get(i);
int peer_port = peerPort.get(i);
if (hasPeer(peer_ip, peer_port)) {
if (addKnownPeersInList) {
try {
Peer peer = getPeer(peer_ip, peer_port);
result.add(peer);
} catch (PeerManagerException e) {
e.printStackTrace();
}
}
continue;
}
try {
result.add(newPeer(peer_ip, peer_port, peerSource));
} catch (PeerManagerException e) {
e.printStackTrace();
}
}
return result;
}
public void sendMessage(Peer peer, String message) throws PeerManagerException{
if (!peer.isConnected())
throw new PeerManagerException("Peer not connected");
_network_manager.sendMessage(peer.getIP(), peer.getPort(), message);
}
public void receivedMessage(String ip, int port, String message) {
Peer peer;
try {
peer = getPeer(ip, port);
notifyPeerMessage(peer, message);
} catch (PeerManagerException e) {
e.printStackTrace();
}
}
public void receivedCaptchaImage(String ip, int port, ByteBuffer image) {
Peer peer;
try {
peer = getPeer(ip, port);
notifyPeerCaptchaImage(peer, image);
} catch (PeerManagerException e) {
e.printStackTrace();
}
}
public void receivedCaptchaStatusAnswer(String ip, int port, byte answer) {
Peer peer;
try {
peer = getPeer(ip, port);
notifyPeerCaptchaStatusAnswer(peer, answer);
} catch (PeerManagerException e) {
e.printStackTrace();
}
}
private void notifyNewPeer(Peer peer) {
for(PeerManagerListener listener : listener_list)
try {
listener.newPeer(peer);
}catch(Throwable t) {
t.printStackTrace();
}
}
private void notifyPeerRemoved(Peer peer) {
for(PeerManagerListener listener : listener_list)
try {
listener.peerRemoved(peer);
}catch(Throwable t) {
t.printStackTrace();
}
}
private void notifyPeerConnecting(Peer peer) {
for(PeerManagerListener listener : listener_list)
try {
listener.peerConnecting(peer);
}catch(Throwable t) {
t.printStackTrace();
}
}
private void notifyPeerConnected(Peer peer) {
for(PeerManagerListener listener : listener_list)
try {
listener.peerConnected(peer);
}catch(Throwable t) {
t.printStackTrace();
}
}
private void notifyPeerDisconnected(Peer peer) {
for(PeerManagerListener listener : listener_list)
try {
listener.peerDisconnected(peer);
}catch(Throwable t) {
t.printStackTrace();
}
}
private void notifyPeerConnectingFailed(Peer peer, Throwable cause) {
for(PeerManagerListener listener : listener_list)
try {
listener.peerConnectingFailed(peer, cause);
}catch(Throwable t) {
t.printStackTrace();
}
}
private void notifyPeerMessage(Peer peer, String message) {
for(PeerManagerListener listener : listener_list)
try {
listener.peerMessage(peer, message);
}catch(Throwable t) {
t.printStackTrace();
}
}
private void notifyPeerCaptchaImage(Peer peer, ByteBuffer image) {
for(PeerManagerListener listener : listener_list)
try {
listener.peerCaptchaImage(peer, image);
}catch(Throwable t) {
t.printStackTrace();
}
}
private void notifyPeerCaptchaStatusAnswer(Peer peer, byte answer) {
for(PeerManagerListener listener : listener_list)
try {
listener.peerCaptchaStatusAnswer(peer, answer);
}catch(Throwable t) {
t.printStackTrace();
}
}
public void bannedNode(int ipAsInt) {
Collection<Peer> ps = peers.values();
for( Peer p : ps )
if( p.getIPAsInt() == ipAsInt ) {
try {
this.disconnect( p );
}catch( Throwable cause ) {
cause.printStackTrace();
}
}
}
}