def _on_message(self, ch, method, properties, body):
        print "message received!"

        data = json.loads(api.decrypt_msg(
            AppModel.objects(nid=self.nid).scalar('private_key').get(), 
            body))

        print data  
        server_id = AppModel.objects(nid=self.nid).scalar('master_sid').get()

        ## access to the shared resource has been permitted
        if (data['action'] == _ACTION_KEYS[8]):
            if server_id == data['by']:     #   for minimum integrity
                #   this data format is to save contents by 'add_comments' function
                #   send all data that is locally stored then it will be filtered to store to
                #   the shared resource inside 'add_comments' function
                data = {
                    'permission':True,      #   this action would be only triggered with master server's permission
                    'nid':self.nid,
                    'sid':data['by'],
                    'content':map(lambda x:{
                            'by':x.by,
                            'created_at':x.timestamp,
                            'comment':x.comment,
                            'session_id':x.session_id
                        }, CommentReplicaModel.objects.all())
                }

                app_api.add_comments(**data)  
    def _store_server_info(self, server_data, nid):
        try:
            AppModel.objects(nid=nid).update_one(
                master_sid=server_data['server_id'],
                server_public_key=server_data['server_public_key'],
                common_key_private=server_data['common_key_private'])

            print "server info updated"
            self._send_heartbeat(server_data['server_id'], nid)

        except:
            logger.error("this client has no information of master server")
def _delete_nodes(target_nids, my_nid):
    n_obj = AppModel.objects(nid=my_nid)

    if not isinstance(target_nids, list):
        if n_obj.update_one(pull__nodes__nid=target_nids)>0:
            n_obj.get().save()
            logger.info(target_nids+" deleted")
        else:
            logger.error("deleted failed")

    else:
        if n_obj.update(pull_all__nodes=
            map(lambda x:{
                'nid':str(x)
            }, target_nids))>0:
            n_obj.get().save()

            logger.info("deleted many")
        else:
            logger.error("deleted many failed")

    nodes = n_obj.get().nodes
    for node in nodes:
        node_obj = json.loads(node.to_json())
        print "id: {}".format(node_obj['_id'])
def _add_nodes(nodes, nid):
    if nodes:
        active_nodes=[]
        for node in nodes:
            if api.uuid_to_obj(node['nid'] is None or api.uuid_to_obj(nid) is None):
                logger.error("wrong type of user id")

            elif node['nid'] != nid:
                active_nodes.append(NodeModel(
                    nid=node['nid'], 
                    public_key=node['public_key']))

            else:
                #print "it's yourself"
                pass

        if active_nodes:
            n_obj = AppModel.objects(nid=nid) 
            n_obj.update(add_to_set__nodes=active_nodes)
            n_obj.get().save()

            logger.info("new node joined, current active node list") 
            nodes = n_obj.get().nodes

            for node in nodes:
                node_obj = json.loads(node.to_json())
                print "id: {}".format(node_obj['_id'])

    else:
        logger.error("nodes has no element")
    def execute_command(self, encrypted_action):
        print "server calling rpc"
        
        data = json.loads(
            api.decrypt_msg(self.model.private_key, encrypted_action))

        action = data['action']
        print action

        ## response from master server has arrived, stop trying to reach other servers
        if self.timer.is_alive():
            self.timer.stop()

        ## server sent info
        if (action == api._ACTION_KEYS[0]):
            self._store_server_info(data['data'], self.model.nid)

        ## add comments 
        elif(action == api._ACTION_KEYS[5]):
            data['nid'] = self.model.nid
            """data['content'].update(
                    {
                    'nid':self.model.nid,
                    'sid':data['sid']
                    })"""
        
            return api.encrypt_msg(AppModel.objects(
                nid=self.model.nid).get().server_public_key,
                    app_api.add_comments(**data))
    def _on_message(self, ch, method, properties, body):
        #try:
        print "fanout message"
        data = json.loads(api.decrypt_msg(
            AppModel.objects(nid=self.nid).scalar('common_key_private').get(), 
            body))     
        #logger.info(data['action'])
        ## A node joined the system
        if (data['action'] == _ACTION_KEYS[1]):
            app_api._add_nodes(data['data'], self.nid)

        ## delete nodes left the system
        elif (data['action'] == _ACTION_KEYS[2]):
            if not isinstance(data['data'], list):
                app_api._delete_nodes(data['data']['nid'], self.nid)
            else:
                app_api._delete_nodes(data['data'], self.nid)

        ## common key timeout, needs to be updated
        elif (data['action'] == _ACTION_KEYS[3]):
            if app_api._refresh_common_key(self.nid, 
                data['data']['common_key_private']) == False:
                logger.error("refreshing common key failed")
            else:
                logger.info("common key refresehd...")
    def _send_heartbeat(self, server_id, nid):
        retry = 0
        while retry < api.MAX_TRY:
            try:
                proxy = callme.Proxy(
                    server_id=server_id, 
                    amqp_host="localhost", 
                    amqp_port=5672, 
                    timeout=3)
                proxy.use_server(server_id).receive_heartbeats(nid, server_id)
                # try again
                retry = api.MAX_TRY

                ## record heartbeat locally as well
                AppModel.objects(nid=nid).update_one(last_access=api._get_current_time())
                is_master_failing = False
                    # try daemon thread
                #threading.Timer(3, _send_heart_beat).setDaemon(True)
                threading.Timer(api.CHECK_HEARTBEAT, self._send_heartbeat, [server_id, nid]).start()

            except:
                retry += 1
                is_master_failing = True
                print "Server: {} is not responding, trying {} times...".format(server_id, str(retry))

                #print "Master server is not responding"
                

        ## master server failed
        if is_master_failing:
            self.timer = ConnTimeout(api._TIMEOUT,
                        app_api._send_handshake_msg,
                        servers=3, 
                        args=[nid, 
                        AppModel.objects(nid=nid).get().public_key, 
                        AppModel.objects(nid=nid).get().created_at.isoformat()])
            self.timer.start()


            """_send_handshake_msg(
def _refresh_common_key(nid, key):
    if AppModel.objects(nid=nid).update_one(common_key_private=key)>0:
        return True
    else:
        return False
from web_server.general_api.connection_timeout import ConnTimeout
from client_model import AppModel
from app_core import Node
from server_listener import BroadcastListener, MessageListener
from mongoengine import *


MSG_FROM_NODES = "msg_from_nodes"
## if this file is directly ran by python
if __name__ == "__main__":

    connect('chatchat')

    private_key = api.generate_private_key()
    m_AppModel = AppModel(
        public_key=api.generate_public_key(private_key).exportKey().decode('utf-8'), 
        nid=str(api.__uuid_generator_1()), 
        private_key=private_key.exportKey().decode('utf-8'))

    ## this will execute document.insert()
    m_AppModel.save()

    # node instance
    mNode = Node(pickle.dumps(m_AppModel))
    mNode.start()

    # listener for receiving broadcasted msg from the master server
    mBroadcastListener = BroadcastListener(m_AppModel.nid)
    mBroadcastListener.start()

    mMessageListener = MessageListener(m_AppModel.nid)
    mMessageListener.start()