def echo_case(self, echo_msg):
        """ (ECHOMessage,) -> None
        
        An execution when receiving an echo message. 
        """
        #TODO: assume that we won't receive an echo if we didn't initiate an explorer

        # get variables from a message
        invoke_id = echo_msg.get_id()
        sender_name = echo_msg.get_sender_name()
        logging.debug(
            "From {0} with invoke id {1}, received an echo message".format(
                sender_name, invoke_id))

        # get the aggregator from a collection
        aggregator = self.aggregator_collections[invoke_id]
        # perform aggregate
        aggregator.results = self.executor.aggregate(
            aggregator.results, echo_msg.get_data(), aggregator.limit,
            aggregator.aggregation_function_list)

        # N := N-{from}
        aggregator.remove_neighbor(sender_name)

        # if N = zero_set
        if len(aggregator.neighbors) == 0:

            # return an echo message to parent
            return_msg = ECHOMessage(ECHOMessage.MSG_TYPE_ECHO, invoke_id,
                                     NAME, aggregator.results)
            # return
            self.senders[aggregator.parent].send(return_msg.serialize())
            #clean up
            self.aggregator_collections[invoke_id] = None
            del aggregator
    def invoke_case(self, invoke_msg):
        """ (ECHOMessage,) -> None
        
        An execution when receiving an invoke message. 
        """
        #TODO: assume that we won't invoke the same ID *

        # get variables from a message
        invoke_id = invoke_msg.get_id()
        parent = invoke_msg.get_sender_name()

        # create an aggregator object
        aggregator = EchoAggregator(NEIGHBOR_LIST, parent)
        # save it for future reference
        self.aggregator_collections[invoke_id] = aggregator

        #if N != zero_set
        if len(aggregator.neighbors) != 0:
            #change an invoke message to an explorer message
            exp_msg = invoke_msg
            exp_msg.msg_type = ECHOMessage.MSG_TYPE_EXP
            exp_msg.sender = NAME

            # send explorer messages
            for host in aggregator.neighbors:
                self.senders[host].send(exp_msg.serialize())

            # A.initiate()
            # get the query object
            query = invoke_msg.get_data()
            # add more information to echo aggregator
            aggregator.limit = query.parameters['limit']
            aggregator.aggregation_function_list = query.aggregation_function_list
            # execute the result
            results = self.executor.execute(query)
            # store local results
            aggregator.results = results
        else:
            # A.initiate()
            # get the query object
            query = invoke_msg.get_data()
            # add more information to echo aggregator
            aggregator.limit = query.parameters['limit']
            aggregator.aggregation_function_list = query.aggregation_function_list
            # execute the result
            results = self.executor.execute(query)
            # create a return message
            return_msg = ECHOMessage(ECHOMessage.MSG_TYPE_RETURN, invoke_id,
                                     NAME, results)
            # return
            self.senders[parent].send(return_msg.serialize())
            # clean up
            self.aggregator_collections[invoke_id] = None
            del aggregator
    def exp_case(self, exp_msg):
        """ (ECHOMessage,) -> None
        
        An execution when receiving an explorer message. 
        """
        # get variables from a message
        invoke_id = exp_msg.get_id()
        sender_name = exp_msg.get_sender_name()
        logging.debug(
            "From {0} with invoke id {1}, received an explorer message".format(
                sender_name, invoke_id))

        # create a socket to parent if not exist
        if sender_name not in self.senders:
            self.senders[sender_name] = self.context.socket(zmq.PUSH)
            self.senders[sender_name].connect("tcp://{0}:{1}".format(
                sender_name, DISPATCHER_PORT))

        #  get a aggregator object from the collection. if not exist, create it
        try:
            aggregator = self.aggregator_collections[invoke_id]
        except KeyError:
            aggregator = EchoAggregator(NEIGHBOR_LIST, sender_name)

        # N := N-{from}
        aggregator.remove_neighbor(sender_name)

        # if not visited
        if invoke_id not in self.aggregator_collections:
            # save it for future reference
            self.aggregator_collections[invoke_id] = aggregator

            # create local result, and store it locally
            # A.initiate()
            # get a query object
            query = exp_msg.get_data()
            # add more information to echo aggregator
            aggregator.limit = query.parameters['limit']
            aggregator.aggregation_function_list = query.aggregation_function_list

            # if there are neighbors
            if len(aggregator.neighbors) != 0:
                # change a sender name of a explorer message
                exp_msg.sender = NAME
                # broadcast explorer messages
                for host in aggregator.neighbors:
                    self.senders[host].send(exp_msg.serialize())

                # execute
                results = self.executor.execute(query)
                # store the result
                aggregator.results = results

            # if it is a leaf
            else:
                # execute
                results = self.executor.execute(query)
                # create a return message
                echo_msg = ECHOMessage(ECHOMessage.MSG_TYPE_ECHO, invoke_id,
                                       NAME, results)
                # send to parent
                self.senders[aggregator.parent].send(echo_msg.serialize())
                #clean up
                self.aggregator_collections[invoke_id] = None
                del aggregator
        # visited and  N = zero_set
        else:
            if len(aggregator.neighbors) == 0:

                # return an echo message to parent
                echo_msg = ECHOMessage(ECHOMessage.MSG_TYPE_ECHO, invoke_id,
                                       NAME, aggregator.results)
                # return
                self.senders[aggregator.parent].send(echo_msg.serialize())
                #clean up
                self.aggregator_collections[invoke_id] = None
                del aggregator