/*
* Copyright 2009 Thomas Bocek
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package net.tomp2p.rpc;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import net.tomp2p.connection2.ChannelCreator;
import net.tomp2p.connection2.ConnectionBean;
import net.tomp2p.connection2.ConnectionConfiguration;
import net.tomp2p.connection2.PeerBean;
import net.tomp2p.connection2.RequestHandler;
import net.tomp2p.futures.FutureResponse;
import net.tomp2p.message.Message2;
import net.tomp2p.message.TrackerData;
import net.tomp2p.message.Message2.Type;
import net.tomp2p.message.NeighborSet;
import net.tomp2p.peers.Number160;
import net.tomp2p.peers.PeerAddress;
import net.tomp2p.storage.Data;
import net.tomp2p.storage.TrackerStorage.ReferrerType;
import net.tomp2p.utils.CacheMap;
import net.tomp2p.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PeerExchangeRPC extends DispatchHandler {
private static final Logger LOG = LoggerFactory.getLogger(PeerExchangeRPC.class);
public static final byte PEX_COMMAND = 10;
public static final int SENT_PEERS_CACHE_SIZE = 1000;
// since PEX is push based, each peer needs to keep track what was sent to
// whom.
// private final Map<Number160, Set<PeerAddress>> sentPeers;
/**
* Create a PEX handler that sends message using fire and forget.
*
* @param peerBean
* The peer bean
* @param connectionBean
* The connection bean
*/
public PeerExchangeRPC(final PeerBean peerBean, final ConnectionBean connectionBean) {
super(peerBean, connectionBean, PEX_COMMAND);
// sentPeers = new CacheMap<Number160, Set<PeerAddress>>(SENT_PEERS_CACHE_SIZE, true);
}
/**
* Peer exchange (PEX) information about other peers from the swarm, to not ask the primary trackers too often. This
* is an RPC.
*
* @param remotePeer
* The remote peer to send this request
* @param locationKey
* The location key
* @param domainKey
* The domain key
* @param isReplication
* Set to true if the PEX is started as replication. This means that this peer learned that an other peer
* is closer and sends tracker information to that peer.
* @param channelCreator
* The channel creator that creates connections
* @param forceTCP
* Set to true if the communication should be TCP, default is UDP
* @return The future response to keep track of future events
*/
public FutureResponse peerExchange(final PeerAddress remotePeer, final Number160 locationKey,
final Number160 domainKey, final boolean isReplication, final ChannelCreator channelCreator,
final ConnectionConfiguration connectionConfiguration) {
final Message2 message = createMessage(remotePeer, PEX_COMMAND, isReplication ? Type.REQUEST_FF_2
: Type.REQUEST_FF_1);
TrackerData peers;
if (isReplication) {
peers = peerBean().trackerStorage().meshPeers(locationKey, domainKey);
LOG.debug("we got stored meshPeers size: {}", peers);
} else {
peers = peerBean().trackerStorage().activePeers(locationKey, domainKey);
LOG.debug("we got stored activePeers size: {}", peers);
}
if (peers == null) {
//future is success as we did not do PEX, since its not necessary
return new FutureResponse(null).setResponse(null);
}
peers = Utils.limit(peers, TrackerRPC.MAX_MSG_SIZE_UDP);
message.setKey(locationKey);
message.setKey(domainKey);
if (peers.size() > 0) { // || removed.size() > 0)
LOG.debug("sent ({}) to {} / {}", message.getSender().getPeerId(), remotePeer.getPeerId(),
peers.size());
message.setTrackerData(peers);
FutureResponse futureResponse = new FutureResponse(message);
final RequestHandler<FutureResponse> requestHandler = new RequestHandler<FutureResponse>(
futureResponse, peerBean(), connectionBean(), connectionConfiguration);
if (!connectionConfiguration.isForceTCP()) {
return requestHandler.fireAndForgetUDP(channelCreator);
} else {
return requestHandler.fireAndForgetTCP(channelCreator);
}
} else {
// we have nothing to deliver
FutureResponse futureResponse = new FutureResponse(message);
futureResponse.setResponse();
return futureResponse;
}
}
@Override
public Message2 handleResponse(final Message2 message, final boolean sign) throws Exception {
if (!((message.getType() == Type.REQUEST_FF_1 || message.getType() == Type.REQUEST_FF_2) && message
.getCommand() == PEX_COMMAND)) {
throw new IllegalArgumentException("Message content is wrong");
}
Number160 locationKey = message.getKey(0);
Number160 domainKey = message.getKey(1);
TrackerData tmp = message.getTrackerData(0);
if (tmp != null && tmp.size() > 0 && locationKey != null && domainKey != null) {
final PeerAddress referrer = message.getSender();
for (Map.Entry<PeerAddress, Data> entry : tmp.getPeerAddresses().entrySet()) {
PeerAddress trackerEntry = entry.getKey();
peerBean().trackerStorage().putReferred(locationKey, domainKey, trackerEntry, referrer,
entry.getValue(),
message.getType() == Type.REQUEST_FF_1 ? ReferrerType.ACTIVE : ReferrerType.MESH);
LOG.debug("Adding {} to the map. I'm {}", entry.getKey(), message.getRecipient());
}
/*
* if (removedKeys != null) { for (Number160 key : removedKeys) {
* peerBean().trackerStorage().removeReferred(locationKey, domainKey, key, referrer); } }
*/
}
return message;
}
}