Package net.tomp2p.rpc

Source Code of net.tomp2p.rpc.NeighborRPC

/*
* 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.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;

import net.tomp2p.connection.ChannelCreator;
import net.tomp2p.connection.ConnectionBean;
import net.tomp2p.connection.ConnectionConfiguration;
import net.tomp2p.connection.PeerBean;
import net.tomp2p.connection.PeerConnection;
import net.tomp2p.connection.PeerException;
import net.tomp2p.connection.PeerException.AbortCause;
import net.tomp2p.connection.RequestHandler;
import net.tomp2p.connection.Responder;
import net.tomp2p.futures.BaseFutureAdapter;
import net.tomp2p.futures.FutureResponse;
import net.tomp2p.message.KeyCollection;
import net.tomp2p.message.Message;
import net.tomp2p.message.Message.Type;
import net.tomp2p.message.NeighborSet;
import net.tomp2p.peers.Number160;
import net.tomp2p.peers.Number320;
import net.tomp2p.peers.Number640;
import net.tomp2p.peers.PeerAddress;
import net.tomp2p.peers.PeerStatusListener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Handles the neighbor requests and replies.
*
* @author Thomas Bocek
*
*/

public class NeighborRPC extends DispatchHandler {
    private static final Logger LOG = LoggerFactory.getLogger(NeighborRPC.class);

    public static final int NEIGHBOR_SIZE = 30;
    public static final int NEIGHBOR_LIMIT = 1000;
   
    public NeighborRPC(final PeerBean peerBean, final ConnectionBean connectionBean) {
        this(peerBean, connectionBean, true);
    }

    /**
     * Setup the RPC and register for incoming messages.
     *
     * @param peerBean
     *            The peer bean
     * @param connectionBean
     *            The connection bean
     */
    public NeighborRPC(final PeerBean peerBean, final ConnectionBean connectionBean, boolean register) {
        super(peerBean, connectionBean);
        if(register) {
            register(RPC.Commands.NEIGHBOR.getNr());
        }
    }

    /**
     * Requests close neighbors from the remote peer. The remote peer may idicate if the data is present on that peer.
     * This is an RPC.
     *
     * @param remotePeer
     *            The remote peer to send this request
     * @param searchValues
     *            The values to search for in the storage
     *
     * @param type
     *            The type of the neighbor request:
     *            <ul>
     *            <li>REQUEST_1 for NEIGHBORS means check for put (no digest) for tracker and storage</li>
     *            <li>REQUEST_2 for NEIGHBORS means check for get (with digest) for storage</li>
     *            <li>REQUEST_3 for NEIGHBORS means check for get (with digest) for tracker</li>
     *            <li>REQUEST_4 for NEIGHBORS means check for put (with digest) for task</li>
     *            </ul>
     * @param channelCreator
     *            The channel creator that creates connections
     * @param configuration
     *            The client side connection configuration
     * @return The future response to keep track of future events
     */
    public FutureResponse closeNeighbors(final PeerAddress remotePeer, final SearchValues searchValues,
            final Type type, final ChannelCreator channelCreator, final ConnectionConfiguration configuration) {
        Message message = createMessage(remotePeer, RPC.Commands.NEIGHBOR.getNr(), type);
        if (!message.isRequest()) {
            throw new IllegalArgumentException("The type must be a request");
        }
       
        message.key(searchValues.locationKey());
        message.key(searchValues.domainKey() == null ? Number160.ZERO : searchValues.domainKey());
       
        if(searchValues.from() !=null && searchValues.to()!=null) {
          Collection<Number640> collection = new ArrayList<Number640>(2);
          collection.add(searchValues.from());
          collection.add(searchValues.to());
          KeyCollection keyCollection = new KeyCollection(collection);
          message.keyCollection(keyCollection);
        } else {
          if (searchValues.contentKey() != null) {
            message.key(searchValues.contentKey());
          }
       
          if (searchValues.keyBloomFilter() != null) {
            message.bloomFilter(searchValues.keyBloomFilter());
          }
          if (searchValues.contentBloomFilter() != null) {
            message.bloomFilter(searchValues.contentBloomFilter());
          }
        }
        return send(message, configuration, channelCreator);
    }
   
    private FutureResponse send(final Message message, final ConnectionConfiguration configuration, final ChannelCreator channelCreator) {
      FutureResponse futureResponse = new FutureResponse(message);
      futureResponse.addListener(new BaseFutureAdapter<FutureResponse>() {
      @Override
            public void operationComplete(FutureResponse future) throws Exception {
              if(future.isSuccess()) {
                Message response = future.responseMessage();
                if(response != null) {
                  NeighborSet ns = response.neighborsSet(0);
                  if(ns!=null) {
                    for(PeerAddress neighbors:ns.neighbors()) {
                      synchronized (peerBean().peerStatusListeners()) {
                        for (PeerStatusListener peerStatusListener : peerBean().peerStatusListeners()) {
                          peerStatusListener.peerFound(neighbors, response.sender(), null);
                        }
                      }
                    }
                  }
                }
              }
            }
    });
        RequestHandler<FutureResponse> request = new RequestHandler<FutureResponse>(futureResponse,
                peerBean(), connectionBean(), configuration);

        if (!configuration.isForceTCP()) {
            return request.sendUDP(channelCreator);
        } else {
            return request.sendTCP(channelCreator);
        }
     
    }

    @Override
    public void handleResponse(final Message message, PeerConnection peerConnection, final boolean sign, Responder responder) throws IOException {
        if (message.keyList().size() < 2) {
            throw new IllegalArgumentException("We need the location and domain key at least");
        }
        if (!(message.type() == Type.REQUEST_1 || message.type() == Type.REQUEST_2
                || message.type() == Type.REQUEST_3 || message.type() == Type.REQUEST_4)
                && (message.command() == RPC.Commands.NEIGHBOR.getNr())) {
            throw new IllegalArgumentException("Message content is wrong");
        }
        Number160 locationKey = message.key(0);
        Number160 domainKey = message.key(1);
       
        Collection<PeerAddress> neighbors = getNeighbors(locationKey, NEIGHBOR_SIZE);
        if(neighbors == null) {
            //return empty neighbor set
            Message response = createResponseMessage(message, Type.NOT_FOUND);
            response.neighborsSet(new NeighborSet(-1, Collections.<PeerAddress>emptyList()));
            responder.response(response);
            return;
        }
       
        // Create response message and set neighbors
        final Message responseMessage = createResponseMessage(message, Type.OK);
       
        LOG.debug("found the following neighbors {}", neighbors);
        NeighborSet neighborSet = new NeighborSet(NEIGHBOR_LIMIT, neighbors);
        responseMessage.neighborsSet(neighborSet);
        // check for fastget, -1 if, no domain provided, so we cannot
        // check content length, 0 for content not here , > 0 content here
        // int contentLength = -1;
        Number160 contentKey = message.key(2);
        SimpleBloomFilter<Number160> keyBloomFilter = message.bloomFilter(0);
        SimpleBloomFilter<Number160> contentBloomFilter = message.bloomFilter(1);
        KeyCollection keyCollection = message.keyCollection(0);
        // it is important to set an integer if a value is present
        boolean isDigest = message.type() != Type.REQUEST_1;
        if (isDigest) {
            if (message.type() == Type.REQUEST_2) {
                final DigestInfo digestInfo;
                if (peerBean().digestStorage() == null) {
                  //no storage to search
                  digestInfo = new DigestInfo();
                }
                else if (contentKey != null && locationKey!=null && domainKey!=null) {
                  Number320 locationAndDomainKey = new Number320(locationKey, domainKey);
                    Number640 from = new Number640(locationAndDomainKey, contentKey, Number160.ZERO);
                    Number640 to = new Number640(locationAndDomainKey, contentKey, Number160.MAX_VALUE);
                    digestInfo = peerBean().digestStorage().digest(from, to, -1, true);
                } else if ((keyBloomFilter != null || contentBloomFilter != null&& locationKey!=null && domainKey!=null) {
                  Number320 locationAndDomainKey = new Number320(locationKey, domainKey);
                    digestInfo = peerBean().digestStorage().digest(locationAndDomainKey, keyBloomFilter,
                            contentBloomFilter, -1, true, true);
                } else if (keyCollection!=null && keyCollection.keys().size() == 2) {
                  Iterator<Number640> iterator = keyCollection.keys().iterator();
                  Number640 from = iterator.next();
                  Number640 to = iterator.next();
                  digestInfo = peerBean().digestStorage().digest(from, to, -1, true);
                } else if (locationKey!=null && domainKey!=null){
                  Number320 locationAndDomainKey = new Number320(locationKey, domainKey);
                    Number640 from = new Number640(locationAndDomainKey, Number160.ZERO, Number160.ZERO);
                    Number640 to = new Number640(locationAndDomainKey, Number160.MAX_VALUE, Number160.MAX_VALUE);
                    digestInfo = peerBean().digestStorage().digest(from, to, -1, true);
                } else {
                  LOG.warn("did not search for anything");
                  digestInfo = new DigestInfo();
                }
                responseMessage.intValue(digestInfo.size());
                responseMessage.key(digestInfo.keyDigest());
                responseMessage.key(digestInfo.contentDigest());
            } else if (message.type() == Type.REQUEST_3) {
              final DigestInfo digestInfo;
              if(peerBean().digestTracker() == null) {
                //no tracker to search
                digestInfo = new DigestInfo();
              } else {
                digestInfo = peerBean().digestTracker().digest(locationKey, domainKey, contentKey);
                if (digestInfo.size() == 0) { 
                  LOG.debug("No entry found on peer {}", message.recipient());
                }
              }
                responseMessage.intValue(digestInfo.size());
            }
            else if (message.type() == Type.REQUEST_4) {
              synchronized (peerBean().peerStatusListeners()) {
                for (PeerStatusListener peerStatusListener : peerBean().peerStatusListeners()) {
              peerStatusListener.peerFailed(message.sender(), new PeerException(AbortCause.SHUTDOWN, "shutdown"));
            }
              }
            }
             
        }
        responder.response(responseMessage);
    }

    /**
     * TODO: explain why protected method here.
     */
    protected Collection<PeerAddress> getNeighbors(Number160 id, int atLeast) {
        return peerBean().peerMap().closePeers(id, atLeast);
    }

    /**
     * The search values for fast get. You can either provide one content key. If you want to check for multiple keys,
     * use the content key bloom filter. You can also check for values with a bloom filter.
     *
     * @author Thomas Bocek
     *
     */

    public static class SearchValues {
        private final SimpleBloomFilter<Number160> keyBloomFilter;
        private final SimpleBloomFilter<Number160> contentBloomFilter;
       
        private final Number160 locationKey;
        private final Number160 domainKey;
        private final Number160 contentKey;
       
        private final Number640 from;
        private final Number640 to;

        /**
         * Searches for all content keys.
         *
         * @param locationKey
         *            The location key
         * @param domainKey
         *            The domain key
         */
        public SearchValues(final Number160 locationKey, final Number160 domainKey) {
            this.locationKey = locationKey;
            this.domainKey = domainKey;
            this.keyBloomFilter = null;
            this.contentBloomFilter = null;
            this.contentKey = null;
            this.from = null;
            this.to = null;
        }

        /**
         * Searches for one content key.
         *
         * @param locationKey
         *            The location key
         * @param domainKey
         *            The domain key
         * @param contentKey
         *            For get() and remove() one can provide the a content key and the remote peer indicates if this key
         *            is on that peer.
         */
        public SearchValues(final Number160 locationKey, final Number160 domainKey, final Number160 contentKey) {
            this.locationKey = locationKey;
            this.domainKey = domainKey;
            this.keyBloomFilter = null;
            this.contentBloomFilter = null;
            this.contentKey = contentKey;
            this.from = null;
            this.to = null;
        }
       
        public SearchValues(Number160 locationKey, Number160 domainKey, Number640 from, Number640 to) {
            this.locationKey = locationKey;
            this.domainKey = domainKey;
            this.keyBloomFilter = null;
            this.contentBloomFilter = null;
            this.contentKey = null;
            this.from = from;
            this.to = to;
        }

        /**
         * Searches for multiple content keys. There may be false positives.
         *
         * @param locationKey
         *            The location key
         * @param domainKey
         *            The domain key
         * @param keyBloomFilter
         *            For get() and remove() one can provide the a bloom filter of content keys and the remote peer
         *            indicates if those keys are on that peer.
         */
        public SearchValues(final Number160 locationKey, final Number160 domainKey,
                final SimpleBloomFilter<Number160> keyBloomFilter) {
            this.locationKey = locationKey;
            this.domainKey = domainKey;
            this.keyBloomFilter = keyBloomFilter;
            this.contentBloomFilter = null;
            this.contentKey = null;
            this.from = null;
            this.to = null;
        }

        /**
         * Searches for content key and values with a bloom filter. There may be false positives.
         *
         * @param locationKey
         *            The location key
         * @param domainKey
         *            The domain key
         * @param keyBloomFilter
         *            For get() and remove() one can provide the a bloom filter of content keys and the remote peer
         *            indicates if those keys are on that peer.
         * @param contentBloomFilter
         *            contentBloomFilter For get() and remove() one can provide the a bloom filter of content values and
         *            the remote peer indicates if those values are on that peer.
         */
        public SearchValues(final Number160 locationKey, final Number160 domainKey,
                final SimpleBloomFilter<Number160> keyBloomFilter,
                final SimpleBloomFilter<Number160> contentBloomFilter) {
            this.locationKey = locationKey;
            this.domainKey = domainKey;
            this.keyBloomFilter = keyBloomFilter;
            this.contentBloomFilter = contentBloomFilter;
            this.contentKey = null;
            this.from = null;
            this.to = null;
        }

    /**
         * @return The location key
         */
        public Number160 locationKey() {
            return locationKey;
        }

        /**
         * @return The domain key
         */
        public Number160 domainKey() {
            return domainKey;
        }

        /**
         * @return The bloom filter for multiple content keys. May contain false positives.
         */
        public SimpleBloomFilter<Number160> keyBloomFilter() {
            return keyBloomFilter;
        }

        /**
         * @return The bloom filter for multiple content values. May contain false positives.
         */
        public SimpleBloomFilter<Number160> contentBloomFilter() {
            return contentBloomFilter;
        }

        /**
         * @return One content key for fast get
         */
        public Number160 contentKey() {
            return contentKey;
        }
       
        public Number640 from() {
          return from;
        }
       
        public Number640 to() {
          return to;
        }
    }
}
TOP

Related Classes of net.tomp2p.rpc.NeighborRPC

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.