1
2
3 try:
4 import psyco
5 psyco.full()
6 except ImportError:
7 pass
8
9 import sys
10 import xml.dom.minidom
11 import traceback
12 import xmpp
13 import threading
14 import thread
15 import Queue
16 import time
17 import MessageReceiver
18 import AID
19 import XMLCodec
20 import ACLParser
21 import Envelope
22 import ACLMessage
23 import BasicFipaDateTime
24 import Behaviour
25 import SL0Parser
26 import fipa
27 import peer2peer as P2P
28 import socialnetwork
29 import RPC
30 import pubsub
31 import bdi
32 from logic import *
33 from kb import *
34
35 import mutex
36 import types
37 import random
38 import string
39 import copy
40 import socket
41 import SocketServer
42 import colors
43 import cPickle as pickle
44 import uuid
45 import json
46
47
48 import DF
49 from content import ContentObject
50 from wui import *
51
52 from xmpp import *
53
54
55 color_none = chr(27) + "[0m"
56 color_black = chr(27) + "[30m"
57 color_red = chr(27) + "[31m"
58 color_green = chr(27) + "[32m"
59 color_brown = chr(27) + "[33m"
60 color_blue = chr(27) + "[34m"
61 color_magenta = chr(27) + "[35m"
62 color_cyan = chr(27) + "[36m"
63 color_light_gray = chr(27) + "[37m"
64 color_dark_gray = chr(27) + "[30;1m"
65 color_bright_red = chr(27) + "[31;1m"
66 color_bright_green = chr(27) + "[32;1m"
67 color_yellow = chr(27) + "[33;1m"
68 color_bright_blue = chr(27) + "[34;1m"
69 color_purple = chr(27) + "[35;1m"
70 color_bright_cyan = chr(27) + "[36;1m"
71 color_white = chr(27) + "[37;1m"
72
73 try:
74 threading.stack_size(64 * 1024)
75 except: pass
79 '''decorator for requiring login in wui controllers'''
80 self = func.__class__
81 def wrap(self, *args, **kwargs):
82 if (not hasattr(self.session,"user_authenticated") or getattr(self.session,"user_authenticated")==False) and self.wui.passwd!=None:
83 name = self.getName().split(".")[0].upper()
84 if name=="ACC": name="SPADE"
85 return "login.pyra", {"name":name,'message':"Authentication is required.", "forward_url":self.session.url}
86 return func(self,*args,**kwargs)
87 wrap.__doc__=func.__doc__
88 wrap.__name__=func.__name__
89 return wrap
90
93 """
94 Abstract Agent
95 only for heritance
96 Child classes: PlatformAgent, Agent
97 """
98
99 - def __init__(self, agentjid, serverplatform, p2p=False):
100 """
101 inits an agent with a JID (user@server) and a platform JID (acc.platformserver)
102 """
103 MessageReceiver.MessageReceiver.__init__(self)
104 self._agent_log = []
105 self._aid = AID.aid(name=agentjid, addresses=["xmpp://"+agentjid])
106 self._jabber = None
107 self._serverplatform = serverplatform
108 self.server = serverplatform
109 self._defaultbehaviour = None
110 self._behaviourList = dict()
111 self._alive = True
112 self._alivemutex = mutex.mutex()
113 self._forceKill = threading.Event()
114 self._forceKill.clear()
115 self.JID=agentjid
116 self.setName(str(agentjid))
117
118 self._debug = False
119 self._debug_filename = ""
120 self._debug_file = None
121 self._debug_mutex = thread.allocate_lock()
122
123 self._messages=[]
124 self._messages_mutex = thread.allocate_lock()
125
126 self.wui = WUI(self)
127 self.wui.registerController("index",self.WUIController_admin)
128 self.wui.registerController("login",self.WUIController_login)
129 self.wui.registerController("logout",self.WUIController_logout)
130 self.wui.registerController("admin", self.WUIController_admin)
131 self.wui.registerController("log", self.WUIController_log)
132 self.wui.registerController("messages",self.WUIController_messages)
133 self.wui.registerController("search",self.WUIController_search)
134 self.wui.registerController("send",self.WUIController_sendmsg)
135 self.wui.registerController("sent",self.WUIController_sent)
136 self.wui.passwd = None
137
138 self._aclparser = ACLParser.ACLxmlParser()
139
140
141
142 self._roster = {}
143 self._socialnetwork = {}
144 self._subscribeHandler = lambda frm,typ,stat,show: False
145 self._unsubscribeHandler = lambda frm,typ,stat,show: False
146
147
148 self._pubsub = pubsub.PubSub(self)
149 self._events = {}
150
151
152 self.kb = KB()
153
154
155 self._waitingForRoster = False
156
157 self.behavioursGo = threading.Condition()
158 self._running = False
159
160
161 self.addBehaviour(P2P.DiscoBehaviour(), Behaviour.MessageTemplate(Iq(queryNS=NS_DISCO_INFO)))
162
163
164 iqsi = Iq()
165 si = iqsi.addChild("si")
166 si.setNamespace("http://jabber.org/protocol/si")
167 self.addBehaviour(P2P.StreamInitiationBehaviour(), Behaviour.MessageTemplate(iqsi))
168
169
170 self.p2p_ready = False
171 self.p2p = p2p
172 self.p2p_routes = {}
173 self.p2p_lock = thread.allocate_lock()
174 self.p2p_send_lock = thread.allocate_lock()
175 self._p2p_failures = 0
176 if p2p:
177 self.registerLogComponent("p2p")
178 self.P2PPORT = random.randint(1025,65535)
179 p2pb = P2P.P2PBehaviour()
180 self.addBehaviour(p2pb)
181
182
183 self.RPC = {}
184 self.addBehaviour(RPC.RPCServerBehaviour(), Behaviour.MessageTemplate(Iq(typ='set',queryNS=NS_RPC)))
185
186
188 self.wui.passwd = str(passwd)
189
191 if hasattr(self.session, "user_authenticated") and getattr(self.session,"user_authenticated")==True:
192 raise HTTP_REDIRECTION, 'index'
193
194 name = self.getName().split(".")[0].upper()
195 if name=="ACC": name="SPADE"
196
197 if password==None:
198 return "login.pyra", {"name":name, "message":"Authentication is required.","forward_url":forward_url}
199 if password!=self.wui.passwd:
200 return "login.pyra", {"name":name, "message":"Password is incorrect. Try again.","forward_url":forward_url}
201
202 else:
203 setattr(self.session,"user_authenticated",True)
204 raise HTTP_REDIRECTION, forward_url
205
210
211 @require_login
213 import types
214 behavs = {}
215 attrs = {}
216 sorted_attrs = []
217 for k in self._behaviourList.keys():
218 behavs[id(k)]=k
219 for attribute in self.__dict__:
220 if eval( "type(self."+attribute+") not in [types.MethodType, types.BuiltinFunctionType, types.BuiltinMethodType, types.FunctionType]" ):
221 if attribute not in ["_agent_log"]:
222 attrs[attribute] = eval( "str(self."+attribute+")" )
223 sorted_attrs = attrs.keys()
224 sorted_attrs.sort()
225 import pygooglechart
226 chart=pygooglechart.QRChart(125,125)
227 chart.add_data(self.getAID().asXML())
228 chart.set_ec('H',0)
229 return "admin.pyra", {"name":self.getName(),"aid":self.getAID(), "qrcode":chart.get_url(), "defbehav":(id(self._defaultbehaviour),self._defaultbehaviour), "behavs":behavs, "p2pready":self.p2p_ready, "p2proutes":self.p2p_routes, "attrs":attrs, "sorted_attrs":sorted_attrs}
230
231 @require_login
233 return "log.pyra", {"name":self.getName(), "log":self.getLog()}
234
235 @require_login
237 index=0
238 mess = {}
239 msc = ""
240 agentslist=[]
241 for ts,m in self._messages:
242 if isinstance(m,ACLMessage.ACLMessage):
243 strm=self._aclparser.encodeXML(m)
244 x = xml.dom.minidom.parseString(strm)
245
246 strm = m.asHTML()
247 frm = m.getSender()
248 if frm!=None: frm = str(frm.getName())
249 else: frm = "Unknown"
250 if "/" in frm: frm=frm.split("/")[0]
251 r = m.getReceivers()
252 if len(r)>=1:
253 to = r[0].getName()
254 else:
255 to = "Unknown"
256 if "/" in to: to=to.split("/")[0]
257 if agents:
258 if to in agents or frm in agents:
259 msc += frm+"->"+to+':'+str(index)+" "+str(m.getPerformative())+'\n'
260 else:
261 msc += frm+"->"+to+':'+str(index)+" "+str(m.getPerformative())+'\n'
262 else:
263 strm=str(m)
264 """strm = strm.replace(">",">")
265 strm = strm.replace("<","<")
266 strm = strm.replace(""",'"')"""
267 x = xml.dom.minidom.parseString(strm)
268 strm = x.toprettyxml()
269
270
271 strm = strm.replace(">", ">")
272 strm = strm.replace("<", "<")
273 strm = strm.replace('"', """)
274 frm = m.getFrom()
275 if frm==None: frm = "Unknown"
276 else: frm = str(frm)
277 if "/" in frm: frm=frm.split("/")[0]
278 to = m.getTo()
279 if to==None: to = "Unknown"
280 else: to = str(to)
281 if "/" in to: to=to.split("/")[0]
282 if agents:
283 if to in agents or frm in agents:
284 msc += frm+"-->"+to+':'+str(index)+' '+str(m.getName())
285 if m.getType(): msc+=" " + str(m.getType())+'\n'
286 elif m.getName()=="message":
287 if m.getAttr("performative"): msc+=" " + str(m.getAttr("performative"))+'\n'
288 else: msc+='\n'
289 else: msc+='\n'
290 else:
291 msc += frm+"-->"+to+':'+str(index)+' '+str(m.getName())
292 if m.getType(): msc+=" " + str(m.getType())+'\n'
293 elif m.getName()=="message":
294 if m.getAttr("performative"): msc+=" " + str(m.getAttr("performative"))+'\n'
295 else: msc+='\n'
296 else: msc+='\n'
297
298 if frm not in agentslist: agentslist.append(frm)
299 if to not in agentslist: agentslist.append(to)
300
301 mess[index]=(ts,strm)
302 index+=1
303
304 return "messages.pyra", {"name":self.getName(), "messages":mess, "diagram": msc, "agentslist":agentslist}
305
306 @require_login
428
429 @require_login
439
440 @require_login
441 - def WUIController_sent(self, receivers=[],performative=None,sender=None,reply_with=None,reply_by=None,reply_to=None,in_reply_to=None,encoding=None,language=None,ontology=None,protocol=None,conversation_id=None,content=""):
468
469
470
474
475 - def DEBUG(self, dmsg, typ="info", component="spade"):
476
477 t = time.ctime()
478 dmsg = dmsg.replace(">",">")
479 dmsg = dmsg.replace("<","<")
480 dmsg = dmsg.replace(""",'"')
481
482 self._debug_mutex.acquire()
483 self._agent_log.append((typ,dmsg,component,t))
484 self._debug_mutex.release()
485
486 if self._debug:
487
488 if typ == "info":
489 print colors.color_none + "DEBUG:[" + component + "] " + dmsg + " , info" + colors.color_none
490 elif typ == "err":
491 print colors.color_none + "DEBUG:[" + component + "] " + color_red + dmsg + " , error" + colors.color_none
492 elif typ == "ok":
493 print colors.color_none + "DEBUG:[" + component + "] " + colors.color_green + dmsg + " , ok" + colors.color_none
494 elif typ == "warn":
495 print colors.color_none + "DEBUG:[" + component + "] " + colors.color_yellow + dmsg + " , warn" + colors.color_none
496
497
498 if self._debug_file:
499 if typ == "info":
500 self._debug_file.write( t + ": [" + component + "] " + dmsg + " , info\n")
501 elif typ == "err":
502 self._debug_file.write( t + ": [" + component + "] " + dmsg + " , error\n")
503 elif typ == "ok":
504 self._debug_file.write( t + ": [" + component + "] " + dmsg + " , ok\n")
505 elif typ == "warn":
506 self._debug_file.write( t + ": [" + component + "] " + dmsg + " , warn\n")
507 self._debug_file.flush()
508
512
514 self._debug = activate
515
517 if not fname:
518 self._debug_filename = self.getName() + ".log"
519 else:
520 self._debug_filename = fname
521
522 try:
523 if self._debug_file:
524 self._debug_file.close()
525 self._debug_file = open(self._debug_filename, "a+")
526 except:
527 self.DEBUG("Could not open file " + self._debug_filename + " as log file", "err")
528
530 l = copy.copy(self._agent_log)
531 l.reverse()
532 return l
533 '''
534 keys = self._agent_log.keys()
535 keys.sort()
536 keys.reverse()
537 l = list()
538 for k in keys:
539 l.append(self._agent_log[k])
540 return l
541 '''
542
546
548 """Creates and returns an empty Content Object"""
549 return ContentObject()
550
552 """
553 presence callback
554 manages jabber stanzas of the 'presence' protocol
555 """
556
557 frm = mess.getFrom()
558 typ = str(mess.getType())
559 status = str(mess.getStatus())
560 show = str(mess.getShow())
561 role = None
562 affiliation = None
563
564 children = mess.getTags(name='x',namespace='http://jabber.org/protocol/muc#user')
565 for x in children:
566 for item in x.getTags(name='item'):
567 role = item.getAttr('role')
568 affiliation = item.getAttr('affiliation')
569
570 try:
571
572 for b in self._behaviourList.keys():
573 b.managePresence(frm, typ, status, show, role, affiliation)
574
575 self._defaultbehaviour.managePresence(frm, typ, status, show, role, affiliation)
576 except Exception, e:
577
578 self.DEBUG(str(e),"err")
579
581 """
582 message callback
583 read the message envelope and post the message to the agent
584 """
585
586 for child in mess.getChildren():
587 if (child.getNamespace() == "jabber:x:fipa") or (child.getNamespace() == u"jabber:x:fipa"):
588
589 ACLmsg = ACLMessage.ACLMessage()
590 ACLmsg._attrs.update(mess.attrs)
591 try:
592
593 del ACLmsg._attrs["from"]
594 except:
595 pass
596 try:
597
598 del ACLmsg._attrs["to"]
599 except:
600 pass
601 ACLmsg.setContent(mess.getBody())
602
603
604
605 if child.getTag("envelope"):
606
607 xc = XMLCodec.XMLCodec()
608 envelope = xc.parse(str(child.getTag("envelope")))
609 if envelope.getFrom():
610 try:
611 ACLmsg.setSender(envelope.getFrom().getStripped())
612 except:
613 ACLmsg.setSender(envelope.getFrom())
614 else:
615 ACLmsg.setSender(AID.aid(str(mess.getFrom().getStripped()), ["xmpp://"+str(mess.getFrom().getStripped())]))
616 if envelope.getIntendedReceiver():
617 for ir in envelope.getIntendedReceiver():
618 ACLmsg.addReceiver(ir)
619 else:
620 ACLmsg.addReceiver(AID.aid(str(mess.getTo().getStripped()), ["xmpp://"+str(mess.getTo())]))
621 else:
622 ACLmsg.setSender(AID.aid(str(mess.getFrom().getStripped()), ["xmpp://"+str(mess.getFrom().getStripped())]))
623 ACLmsg.addReceiver(AID.aid(str(mess.getTo().getStripped()), ["xmpp://"+str(mess.getTo().getStripped())]))
624
625 self._messages_mutex.acquire()
626 timestamp = time.time()
627 self._messages.append((timestamp,ACLmsg))
628 self._messages_mutex.release()
629 self.postMessage(ACLmsg)
630 if raiseFlag: raise xmpp.NodeProcessed
631 return True
632
633
634 self._messages_mutex.acquire()
635 timestamp = time.time()
636 self._messages.append((timestamp,mess))
637 self._messages_mutex.release()
638
639
640 if not self._running:
641 if mess.getName() == "iq":
642
643 if mess.getAttr("type") == "get":
644 q = mess.getTag("query")
645 if q and q.getNamespace() == NS_DISCO_INFO:
646 self.DEBUG("DISCO Behaviour called (offline)","info")
647
648 reply = mess.buildReply("result")
649 if self.p2p_ready:
650 reply.getTag("query").addChild("feature", {"var":"http://jabber.org/protocol/si"})
651 reply.getTag("query").addChild("feature", {"var":"http://jabber.org/protocol/si/profile/spade-p2p-messaging"})
652 self.send(reply)
653 if raiseFlag: raise xmpp.NodeProcessed
654 return True
655
656 if mess.getAttr("type") == "set":
657 q = mess.getTag("si")
658 if q:
659 if mess.getType() == "set":
660 if mess.getTag("si").getAttr("profile") == "http://jabber.org/protocol/si/profile/spade-p2p-messaging":
661
662 if self.p2p_ready:
663
664 if mess.getTag("si").getTag("p2p"):
665 remote_address = str(mess.getTag("si").getTag("p2p").getData())
666 d = {"url":remote_address, "p2p":True}
667 self.p2p_lock.acquire()
668 if self.p2p_routes.has_key(str(mess.getFrom().getStripped())):
669 self.p2p_routes[str(mess.getFrom().getStripped())].update(d)
670 if self.p2p_routes[str(mess.getFrom().getStripped())].has_key("socket"):
671 self.p2p_routes[str(mess.getFrom().getStripped())]["socket"].close()
672 else:
673 self.p2p_routes[str(mess.getFrom().getStripped())] = d
674 self.p2p_lock.release()
675
676
677 reply = mess.buildReply("result")
678 si = reply.addChild("si")
679 si.setNamespace("http://jabber.org/protocol/si")
680 p2p = si.addChild("p2p")
681 p2p.setNamespace('http://jabber.org/protocol/si/profile/spade-p2p-messaging')
682 value = p2p.addChild("value")
683 value.setData(self.getP2PUrl())
684 else:
685
686 reply = mess.buildReply("error")
687 err = reply.addChild("error", attrs={"code":"403","type":"cancel"})
688 err.addChild("forbidden")
689 err.setNamespace("urn:ietf:params:xml:ns:xmpp-stanzas")
690 self.send(reply)
691 if raiseFlag: raise xmpp.NodeProcessed
692 return True
693
694 self.DEBUG("Posting message " + str(mess), "info", "msg")
695 self.postMessage(mess)
696 if raiseFlag: raise xmpp.NodeProcessed
697 return True
698
699
701 """
702 non jabber:x:fipa chat messages callback
703 """
704 pass
705
707 """
708 IQ callback
709 manages jabber stanzas of the 'iq' protocol
710 """
711
712 self.postMessage(mess)
713 self.DEBUG("Jabber Iq posted to agent " + str(self.getAID().getName()),"info")
714
715
717 """
718 returns AID
719 """
720 return self._aid
721
723 """
724 sets a new AID
725 """
726 self._aid = aid
727
730
733
735 """
736 returns the AMS aid
737 """
738 return AID.aid(name="ams." + self._serverplatform, addresses=[ "xmpp://ams."+self._serverplatform ])
739
741 """
742 returns the DF aid
743 """
744 return AID.aid(name="df." + self._serverplatform, addresses=[ "xmpp://df."+self._serverplatform ])
745
747 """
748 returns the MUC JID
749 """
750 return "muc." + self._serverplatform
751
757
758 - def getDomain(self):
759 """
760 returns the SPADE server domain
761 """
762 return self._serverplatform
763
765 return str("spade://"+socket.gethostbyname(socket.gethostname())+":"+str(self.P2PPORT))
766
767
784
785
811
812
813 - def send(self, ACLmsg, method="jabber"):
814 """
815 sends an ACLMessage
816 """
817 self._messages_mutex.acquire()
818 timestamp = time.time()
819 self._messages.append((timestamp,ACLmsg))
820 self._messages_mutex.release()
821
822
823 if isinstance(ACLmsg,xmpp.Iq) or isinstance(ACLmsg,xmpp.Presence) or isinstance(ACLmsg,xmpp.Message):
824 self.jabber.send(ACLmsg)
825 return
826
827 ACLmsg._attrs.update({"method":method})
828
829 if not ACLmsg.getSender():
830 ACLmsg.setSender(self.getAID())
831
832 self._sendTo(ACLmsg, ACLmsg.getReceivers(), method=method.strip())
833
834 - def _sendTo(self, ACLmsg, tojid, method):
835 """
836 sends an ACLMessage to a specific JabberID
837 """
838
839
840 try:
841 if method in ["auto", "p2ppy"]:
842 remaining = copy.copy(tojid)
843 for receiver in tojid:
844 to = None
845 for address in receiver.getAddresses():
846
847 if "xmpp://" in address:
848 to = address.split("://")[1]
849 break
850 if to and self.send_p2p(None, to, method="p2ppy", ACLmsg=ACLmsg):
851
852 remaining.remove(receiver)
853
854 tojid = remaining
855 if not tojid:
856
857 return
858 except Exception, e:
859 self.DEBUG("Could not send through P2PPY: "+str(e), "warn")
860 method = "jabber"
861
862
863 xenv = xmpp.protocol.Node('jabber:x:fipa x')
864 envelope = Envelope.Envelope()
865 generate_envelope = False
866
867
868
869 try:
870 if method=="xmppfipa" or len(ACLmsg.getSender().getAddresses()) > 1 or \
871 "xmpp" not in ACLmsg.getSender().getAddresses()[0]:
872 envelope.setFrom(ACLmsg.getSender())
873 generate_envelope = True
874 except Exception, e:
875 self.DEBUG("Error setting sender: "+ str(e), "err")
876
877 try:
878 for i in ACLmsg.getReceivers():
879
880
881
882
883 if len(i.getAddresses()) > 1 or \
884 "xmpp" not in i.getAddresses()[0]:
885 envelope.addTo(i)
886 generate_envelope = True
887 except Exception, e:
888 self.DEBUG("Error setting receivers: " + str(e),"err")
889
890
891 try:
892
893 for i in ACLmsg.getReplyTo():
894
895
896
897
898 if len(i.getAddresses()) > 1 or \
899 "xmpp" not in i.getAddresses()[0]:
900 envelope.addIntendedReceiver(i)
901 generate_envelope = True
902 except Exception, e:
903 self.DEBUG("Error setting reply-to: " + str(e),"err")
904
905
906 if generate_envelope:
907 xc = XMLCodec.XMLCodec()
908 envxml = xc.encodeXML(envelope)
909 xenv['content-type']='fipa.mts.env.rep.xml.std'
910 xenv.addChild(node=simplexml.NodeBuilder(envxml).getDom())
911
912
913 for to in tojid:
914 isjabber = False
915 for address in to.getAddresses():
916 if "xmpp://" in address:
917
918 jabber_id = address.split("://")[1]
919 isjabber = True
920 break
921 if isjabber and str(self.getDomain()) in jabber_id:
922 jabber_msg = xmpp.protocol.Message(jabber_id, xmlns="")
923 jabber_msg.attrs.update(ACLmsg._attrs)
924 jabber_msg.addChild(node=xenv)
925 jabber_msg["from"]=self.getAID().getName()
926 jabber_msg.setBody(ACLmsg.getContent())
927 else:
928
929 jabber_msg = xmpp.protocol.Message(self.getSpadePlatformJID(), xmlns="")
930 jabber_id = self.getSpadePlatformJID()
931 jabber_msg.attrs.update(ACLmsg._attrs)
932 jabber_msg.addChild(node=xenv)
933 jabber_msg["from"]=self.getAID().getName()
934 jabber_msg.setBody(ACLmsg.getContent())
935
936 if (not self._running and method=="auto") or method=="jabber":
937 self.jabber.send(jabber_msg)
938 continue
939
940
941
942
943 jabber_id = xmpp.JID(jabber_id).getStripped()
944
945 if self.p2p_ready:
946 sent = False
947 self.p2p_send_lock.acquire()
948 try:
949 sent = self.send_p2p(jabber_msg, jabber_id, method=method, ACLmsg=ACLmsg)
950 except Exception, e:
951 self.DEBUG("P2P Connection to "+str(self.p2p_routes)+jabber_id+" prevented. Falling back. "+str(e), "warn")
952 sent = False
953 self.p2p_send_lock.release()
954 if not sent:
955
956 self.DEBUG("P2P failed, try to send it through jabber","warn")
957 jabber_msg.attrs.update({"method":"jabber"})
958 if method in ["auto","p2p","p2ppy"]: self.jabber.send(jabber_msg)
959
960 else:
961
962
963 self.DEBUG("P2P is not available/supported, try to send it through jabber","warn")
964 jabber_msg.attrs.update({"method":"jabber"})
965 if method in ["auto","p2p","p2ppy"]: self.jabber.send(jabber_msg)
966
967
968 - def send_p2p(self, jabber_msg=None, to="", method="p2ppy", ACLmsg=None):
969
970
971 if self.p2p:
972 while not self.p2p_ready:
973 time.sleep(0.1)
974 else:
975
976 self.DEBUG("This agent does not support sending p2p messages", "warn")
977 return False
978
979
980 if not to:
981 if not jabber_msg:
982 return False
983 else:
984 to = str(jabber_msg.getTo())
985 if jabber_msg:
986 self.DEBUG("Trying to send Jabber msg through P2P")
987 elif ACLmsg:
988 self.DEBUG("Trying to send ACL msg through P2P")
989
990
991 try:
992
993 url = self.p2p_routes[to]["url"]
994 except:
995
996 self.DEBUG("P2P: The contact " + str(to) + " is not in our routes. Starting negotiation","warn")
997 self.initiateStream(to)
998 if self.p2p_routes.has_key(to) and self.p2p_routes[to].has_key('p2p'):
999
1000
1001 if self.p2p_routes[to]['p2p'] == False:
1002 try:
1003 t1 = time.time()
1004 t = t1 - self.p2p_routes[to]['failed_time']
1005
1006 if t > 10.0:
1007 self.p2p_lock.acquire()
1008 self.p2p_routes[to]['p2p'] = True
1009 self.p2p_routes[to]['failed_time'] = 0.0
1010 self.p2p_lock.release()
1011 except:
1012
1013 self.DEBUG("P2P: The p2p connection is really faulty","warn")
1014 return False
1015 url = self.p2p_routes[to]["url"]
1016 else:
1017
1018 self.DEBUG("P2P: There is no p2p support for this contact","warn")
1019 return False
1020
1021
1022
1023 s = None
1024 if self.p2p_routes[to].has_key("socket"):
1025 s = self.p2p_routes[to]["socket"]
1026 if not s:
1027
1028 scheme, address = url.split("://",1)
1029 if scheme == "spade":
1030
1031 l = address.split(":",1)
1032 if len(l) > 1:
1033 address = l[0]
1034 port = int(l[1])
1035
1036
1037 connected = False
1038 tries = 2
1039 while not connected and tries > 0:
1040 try:
1041 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1042 s.connect((address, port))
1043 self.p2p_lock.acquire()
1044 self.p2p_routes[to]["socket"] = s
1045 self.p2p_lock.release()
1046 connected = True
1047 except:
1048 tries -= 1
1049 _exception = sys.exc_info()
1050 if _exception[0]:
1051 self.DEBUG("Error opening p2p socket " +'\n'+''.join(traceback.format_exception(_exception[0], _exception[1], _exception[2])).rstrip(),"err")
1052
1053 if not connected:
1054 self.DEBUG("Socket creation failed","warn")
1055 return False
1056
1057
1058 sent = False
1059 tries = 2
1060 while not sent and tries > 0:
1061 try:
1062 if method in ["p2p","auto"]:
1063 jabber_msg.attrs.update({"method":"p2p"})
1064 length = "%08d"%(len(str(jabber_msg)))
1065
1066 s.send(length+str(jabber_msg))
1067 self.DEBUG("P2P message sent through p2p","ok")
1068 elif method in ["p2ppy"]:
1069 ACLmsg._attrs.update({"method":"p2ppy"})
1070 ser = pickle.dumps(ACLmsg)
1071 length = "%08d"%(len(str(ser)))
1072 s.send(length+ser)
1073 self.DEBUG("P2P message sent through p2ppy","ok")
1074 sent = True
1075
1076 except Exception, e:
1077 self.DEBUG("Socket: send failed, threw an exception: " +str(e), "err")
1078 self._p2p_failures += 1
1079
1080 self.p2p_lock.acquire()
1081 s.close()
1082 try:
1083 del s
1084 del self.p2p_routes[to]["socket"]
1085 except: pass
1086 self.p2p_lock.release()
1087
1088 scheme, address = url.split("://",1)
1089 if scheme == "spade":
1090
1091 l = address.split(":",1)
1092 if len(l) > 1:
1093 address = l[0]
1094 port = int(l[1])
1095
1096 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1097 s.connect((address, port))
1098 self.p2p_lock.acquire()
1099 self.p2p_routes[to]["socket"] = s
1100 self.p2p_lock.release()
1101 else:
1102 return False
1103 tries -= 1
1104 if not sent:
1105 self.DEBUG("Socket send failed","warn")
1106 self.p2p_lock.acquire()
1107 self.p2p_routes[to]["p2p"] = False
1108 self.p2p_routes[to]["failed_time"] = time.time()
1109 self.p2p_lock.release()
1110 return False
1111 else:
1112 return True
1113
1115 """
1116 kills the agent
1117 """
1118 self._forceKill.set()
1119
1121 """
1122 returns wether an agent is running or not
1123 """
1124 return self._alive
1125
1126 - def stop(self, timeout=0):
1127 """
1128 Stops the agent execution and blocks until the agent dies
1129 """
1130
1131 self.wui.stop()
1132
1133 self._kill()
1134 if timeout > 0:
1135 to = time.now() + timeout
1136 while self._alive and time.now() < to:
1137 time.sleep(0.1)
1138
1139 else:
1140 while self._alive:
1141 time.sleep(0.1)
1142 return True
1143
1144
1146 return self._forceKill.isSet()
1147
1149 """
1150 setup agent method. configures the agent
1151 must be overridden
1152 """
1153 pass
1154
1156 """
1157 starts the BDI behaviour ONLY
1158 if self is a subclass of bdi.BDIAgent
1159 """
1160 if issubclass(self.__class__, BDIAgent):
1161 self._startBdiBehav()
1162
1164 """
1165 stops the agent
1166 must be overridden
1167 (kind of a "onEnd" for the agent)
1168 """
1169 pass
1170
1171
1173 """
1174 periodic agent execution
1175 """
1176
1177 self._setup()
1178 self.behavioursGo.acquire()
1179 self._running = True
1180 self.behavioursGo.notifyAll()
1181 self.behavioursGo.release()
1182
1183
1184 if (self._defaultbehaviour != None):
1185 self._defaultbehaviour.start()
1186
1187
1188 if self.p2p:
1189 while not self.p2p_ready:
1190 time.sleep(0.1)
1191
1192
1193
1194
1195 while not self.forceKill():
1196 try:
1197
1198 proc = False
1199 toRemove = []
1200 msg = self._receive(block=True, timeout=0.01)
1201 if msg != None:
1202 bL = copy.copy(self._behaviourList)
1203 for b in bL:
1204 t = bL[b]
1205 if (t != None):
1206 if (t.match(msg) == True):
1207 if ((b == types.ClassType or type(b) == types.TypeType) and issubclass(b, Behaviour.EventBehaviour)):
1208 ib = b()
1209 if ib.onetime:
1210 toRemove.append(b)
1211 ib.setAgent(self)
1212 ib.postMessage(msg)
1213 ib.start()
1214 else:
1215 b.postMessage(msg)
1216 proc = True
1217
1218 if (proc == False):
1219
1220 self.DEBUG("Message was not reclaimed by any behaviour. Posting to default behaviour: " + str(msg) + str(bL), "info", "msg")
1221 if (self._defaultbehaviour != None):
1222 self._defaultbehaviour.postMessage(msg)
1223 for beh in toRemove:
1224 self.removeBehaviour(beh)
1225
1226 except Exception, e:
1227 self.DEBUG("Agent " + self.getName() + "Exception in run:" + str(e), "err")
1228 self._kill()
1229
1230 self._shutdown()
1231
1233 """
1234 sets a Behavior as Default
1235 """
1236 class NotAllowed(Exception):
1237 """
1238 Not Allowed Exception: an EventBehaviour cannot be a default behaviour
1239 """
1240 def __init__(self): pass
1241 def __str__(self): return "an EventBehaviour cannot be a default behaviour"
1242
1243 if behaviour.__class__ == Behaviour.EventBehaviour:
1244 raise NotAllowed
1245 self._defaultbehaviour = behaviour
1246 behaviour.setAgent(self)
1247
1249 """
1250 returns the default behavior
1251 """
1252 return self._defaultbehaviour
1253
1255 """
1256 adds a new behavior to the agent
1257 """
1258 if not issubclass(behaviour.__class__, Behaviour.EventBehaviour):
1259
1260 self._behaviourList[behaviour] = copy.copy(template)
1261 behaviour.setAgent(self)
1262 behaviour.start()
1263 else:
1264 self.DEBUG("Adding Event Behaviour "+str(behaviour.__class__))
1265 self._behaviourList[behaviour.__class__] = copy.copy(template)
1266
1268 """
1269 Runs the behaviour offline
1270 Executes its process once
1271 @warning Only for OneShotBehaviour
1272 """
1273 if not issubclass(behaviour.__class__, Behaviour.OneShotBehaviour):
1274 self.DEBUG("Only OneShotBehaviour execution is allowed offline","err")
1275 return False
1276
1277 if not self._running:
1278 try:
1279 behaviour._receive = self._receive
1280 behaviour.myAgent = self
1281 behaviour.onStart()
1282 behaviour._process()
1283 behaviour.onEnd()
1284 del behaviour
1285 return True
1286 except Exception,e:
1287 self.DEBUG("Failed the execution of the OFFLINE behaviour "+str(behaviour)+": "+str(e),"err")
1288 return False
1289 else:
1290 self.addBehaviour(behaviour,template)
1291
1292
1294 """
1295 removes a behavior from the agent
1296 """
1297 if (type(behaviour) not in [types.ClassType,types.TypeType]) and (not issubclass(behaviour.__class__, Behaviour.EventBehaviour)):
1298 behaviour.kill()
1299 try:
1300 self._behaviourList.pop(behaviour)
1301 self.DEBUG("Behaviour removed: " + str(behaviour), "info", "behaviour")
1302 except KeyError:
1303 self.DEBUG("removeBehaviour: Behaviour " + str(behaviour) +"with type " +str(type(behaviour))+ " is not registered in "+str(self._behaviourList),"warn")
1304
1305
1307 """
1308 presence subscription to another agent
1309 """
1310 pass
1311
1313 """
1314 presence unsubscription to another agent
1315 """
1316 pass
1317
1319 """
1320 get list of social agents which have some relation with the agent
1321 """
1322 self._waitingForRoster = True
1323 iq = Iq("get", NS_ROSTER)
1324 self.send(iq)
1325 if not nowait:
1326 while self._waitingForRoster:
1327 time.sleep(0.3)
1328 return self._roster
1329
1330
1331
1332
1333
1334
1352
1353
1354
1371
1372
1373
1390
1391
1392
1394 """
1395 registers a service in the DF
1396 the service template is a DfAgentDescriptor
1397 """
1398
1399 if isinstance(service,DF.Service): DAD=service.getDAD()
1400 else: DAD = service
1401
1402 msg = ACLMessage.ACLMessage()
1403 template = Behaviour.ACLTemplate()
1404 if otherdf and isinstance(otherdf, AID.aid):
1405 template.setSender(otherdf)
1406 else:
1407 template.setSender(self.getDF())
1408 template.setConversationId(msg.getConversationId())
1409 r = str(uuid.uuid4()).replace("-","")
1410 msg.setReplyWith(r)
1411 template.setInReplyTo(r)
1412 t = Behaviour.MessageTemplate(template)
1413 b = fipa.registerServiceBehaviour(msg=msg, DAD=DAD, otherdf=otherdf)
1414 if self._running:
1415
1416 self.addBehaviour(b,t)
1417 b.join()
1418 else:
1419 self.runBehaviourOnce(b,t)
1420
1421 if methodCall and b.result==True:
1422 if not isinstance(service,DF.Service):
1423 self.DEBUG("Could not register RPC Service. It's not a DF.Service class","error")
1424 return False
1425
1426 name = service.getName()
1427 self.DEBUG("Registering RPC service "+ name)
1428 self.RPC[name.lower()] = (service, methodCall)
1429
1430 return b.result
1431
1432
1466
1467
1515
1516
1517
1551
1552
1553
1554
1556 """
1557 invokes a service using jabber-rpc (XML-RPC)
1558 the service template is a DF.Service
1559 if inputs is None, they are extracted from the agent's KB
1560 """
1561
1562 if not isinstance(service,DF.Service):
1563 self.DEBUG("Service MUST be a DF.Service instance",'error')
1564 return False
1565
1566 num = str(uuid.uuid4()).replace("-","")
1567
1568 if inputs==None:
1569 inputs={}
1570 for i in service.getInputs():
1571 r = self.kb.get(str(i))
1572 if r==None:
1573 self.DEBUG("Can not invoke Service, input not found: " + str(i),'error')
1574 return False
1575 self.DEBUG("Adding input: "+str(i)+" = "+str(r))
1576 inputs[i] = r
1577
1578 self.DEBUG("Invoking service " + str(service.getName()) + " with inputs = "+ str(inputs))
1579 b = RPC.RPCClientBehaviour(service,inputs,num)
1580 t = Behaviour.MessageTemplate(Iq(typ="result",attrs={'id':num}))
1581
1582 if self._running:
1583
1584 self.addBehaviour(b,t)
1585 b.join()
1586 return b.result
1587 else:
1588 self.runBehaviourOnce(b,t)
1589 return b.result
1590
1591
1592
1593
1594
1595
1599 r = self._pubsub.subscribe(name,server,jid)
1600 if r[0]=='ok' and behaviour!=None:
1601 if not issubclass(behaviour.__class__, Behaviour.EventBehaviour):
1602 self.DEBUG("Behaviour MUST be an EventBehaviour to subscribe to events.","error","pubsub")
1603 return ("error",["not-event-behaviour"])
1604 self._events[name]=behaviour
1605 n = xmpp.Node(node='<message xmlns="jabber:client"><event xmlns="http://jabber.org/protocol/pubsub#events"><items node="'+name+'" /></event></message>')
1606 template = xmpp.Message(node=n)
1607 mt = Behaviour.MessageTemplate(template)
1608 self.addBehaviour(behaviour,mt)
1609 return r
1616 - def createEvent(self, name, server=None, type='leaf', parent=None, access=None):
1617 return self._pubsub.createNode(name, server=None, type='leaf', parent=None, access=None)
1620
1621
1622
1623
1624
1625
1627 if isinstance(sentence,types.StringType):
1628 try:
1629 if issubclass(Flora2KB.Flora2KB,self.kb.__class__):
1630 self.kb.tell(sentence,type)
1631 except:
1632 self.kb.tell(sentence)
1633 else:
1634 self.kb.tell(sentence)
1635 self._needDeliberate = True
1636
1637
1639 if isinstance(sentence,types.StringType):
1640 try:
1641 if issubclass(Flora2KB.Flora2KB,self.kb.__class__):
1642 self.kb.retract(sentence,type)
1643 except:
1644 self.kb.retract(sentence)
1645 else:
1646 self.kb.retract(sentence)
1647 self._needDeliberate = True
1648
1650 return self.kb.ask(sentence)
1651
1654
1657
1660
1665
1667 self.jabber = socket
1668
1669 self._forceKill = threading.Event()
1670 self._forceKill.clear()
1671 threading.Thread.__init__(self)
1672 self.setDaemon(False)
1673 self._owner = owner
1674
1676 try:
1677 self._forceKill.set()
1678 except:
1679
1680 pass
1681
1683 return self._forceKill.isSet()
1684
1686 """
1687 periodic jabber update
1688 """
1689 while not self.forceKill():
1690 try:
1691 err = self.jabber.Process(0.4)
1692 except Exception, e:
1693 _exception = sys.exc_info()
1694 if _exception[0]:
1695 self._owner.DEBUG( '\n'+''.join(traceback.format_exception(_exception[0], _exception[1], _exception[2])).rstrip(),"err")
1696 self._owner.DEBUG("Exception in jabber process: "+ str(e),"err")
1697 self._owner.DEBUG("Jabber connection failed: "+self._owner.getAID().getName()+" (dying)","err")
1698 self._kill()
1699 self._owner.stop()
1700 err = None
1701
1702 if err == None or err == 0:
1703 self._owner.DEBUG("Agent disconnected: "+self._owner.getAID().getName()+" (dying)","err")
1704 self._kill()
1705 self._owner.stop()
1706
1771
1772 -class Agent(AbstractAgent):
1773 """
1774 This is the main class which may be inherited to build a SPADE agent
1775 """
1776
1777
1778 - def __init__(self, agentjid, password, port=5222, debug=[], p2p=False):
1779 jid = xmpp.protocol.JID(agentjid)
1780 self.server = jid.getDomain()
1781 self.port = port
1782 self.debug = debug
1783 AbstractAgent.__init__(self, agentjid, jid.getDomain(),p2p=p2p)
1784
1785 self.jabber = xmpp.Client(jid.getDomain(), port, debug)
1786
1787
1788 try:
1789 self.DEBUG("Trying to register agent " + agentjid)
1790 if not self._register(password):
1791 self.stop()
1792 except NotImplementedError:
1793 self.DEBUG("NotImplementedError: Could not register agent %s"%(agentjid),"err")
1794 self.stop()
1795 return
1796 except:
1797 self.DEBUG("Could not register agent %s"%(agentjid),"err")
1798 self.stop()
1799 return
1800
1801
1802 self.addBehaviour(socialnetwork.PresenceBehaviour(), Behaviour.MessageTemplate(Presence()))
1803
1804
1805 self.addBehaviour(socialnetwork.RosterBehaviour(), Behaviour.MessageTemplate(Iq(queryNS=NS_ROSTER)))
1806
1807
1808 self._initBdiBehav()
1809
1810 self.DEBUG("Agent %s registered"%(agentjid),"ok")
1811
1812 if not self.__register_in_AMS():
1813 self.DEBUG("Agent " + str(self.getAID().getName()) + " dying ...","err")
1814 self.stop()
1815
1816
1817
1818
1820 if self._socialnetwork.has_key(jid):
1821 if not self._socialnetwork[jid].getPresence():
1822
1823 self._socialnetwork[jid].setPresence(presence)
1824 else:
1825 self._socialnetwork[jid] = socialnetwork.SocialItem(self, jid, presence)
1826
1827
1828 - def _register(self, password, autoregister=True):
1829 """
1830 registers the agent in the Jabber server
1831 """
1832
1833 jid = xmpp.protocol.JID(self._aid.getName())
1834 name = jid.getNode()
1835
1836
1837 tries = 5
1838 while not self.jabber.connect(use_srv=None) and tries >0:
1839 time.sleep(0.005)
1840 tries -=1
1841 if tries <=0 :
1842 self.setDebugToScreen()
1843 self.DEBUG("There is no SPADE platform at " + self.server + " . Agent dying...","err")
1844 return False
1845
1846
1847 if (self.jabber.auth(name,password,"spade") == None):
1848
1849 self.DEBUG("First auth attempt failed. Trying to register","warn")
1850
1851 if (autoregister == True):
1852 xmpp.features.getRegInfo(self.jabber,jid.getDomain())
1853 xmpp.features.register(self.jabber,jid.getDomain(),\
1854 {'username':name, 'password':str(password), 'name':name})
1855
1856
1857 if not self.jabber.reconnectAndReauth():
1858 self.DEBUG("Second auth attempt failed (username="+str(name)+")", "err")
1859 return False
1860 else:
1861 return False
1862
1863 self.DEBUG("Agent %s got authed"%(self._aid.getName()),"ok")
1864
1865 self.jabber.RegisterHandler('message',self._jabber_messageCB)
1866 self.jabber.RegisterHandler('presence',self._jabber_messageCB)
1867 self.jabber.RegisterHandler('iq',self._jabber_messageCB)
1868
1869
1870 self.jabber_process = jabberProcess(self.jabber, owner=self)
1871 self.jabber_process.start()
1872
1873
1874
1875
1876 self.jabber.sendInitPresence()
1877
1878 return True
1879
1880
1882
1883 for b in copy.copy(self._behaviourList):
1884 try:
1885 b.kill()
1886 if "P2PBehaviour" in str(b.__class__):
1887 b.onEnd()
1888 except:
1889 pass
1890
1891 if (self._defaultbehaviour != None):
1892 self._defaultbehaviour.kill()
1893
1894
1895 self.takeDown()
1896
1897 if self._alivemutex.testandset():
1898 if not self.jabber_process.forceKill():
1899 if not self.__deregister_from_AMS():
1900 self.DEBUG("Agent " + str(self.getAID().getName()) + " dying without deregistering itself ...","err")
1901 self.jabber_process._kill()
1902 self._alive = False
1903 self._alivemutex.unlock()
1904
1905 self._kill()
1906
1907
1908
1917
1965
1966
1975
1976
2025
2031
2034
2037
2038 - def addPlan(self, inputs=[], outputs=[],P=[],Q=[],services=[]):
2040
2043
2045 '''func MUST have as input parameter a plan'''
2046 self.bdiBehav.planSelectedCB = func
2047
2049 '''func MUST have as input parameter a goal'''
2050 self.bdiBehav.goalCompletedCB = func
2051
2053 '''func MUST have as input parameter a DF.Service'''
2054 self.bdiBehav.serviceCompletedCB = func
2055