home *** CD-ROM | disk | FTP | other *** search
/ Enter 2004 April / enter-2004-04.iso / files / EVE_1424_100181.exe / service.py < prev    next >
Encoding:
Text File  |  2004-04-20  |  69.7 KB  |  1,509 lines

  1. ##########################################################################################
  2. ## ///////////////////////////////////////////////////////////////////////////////////////
  3. ## //
  4. ## //   service.py
  5. ## //
  6. ## //   Authors:   Eggert J≤n Magn·sson
  7. ## //   Created:   November 2001
  8. ## //   Project:   EVE Server
  9. ## //
  10. ## //   Description:
  11. ## //
  12. ## //      This file contains the implementation of the base for all services in the EVE
  13. ## //      Server. All services inherit from this base clasee and over-ride some functions
  14. ## //
  15. ## //   Dependencies:
  16. ## //
  17. ## //      blue but only for the creation of log channels
  18. ## //
  19. ## //   (c) CCP 2001
  20. ## //
  21. ## //
  22.  
  23. import types
  24. import blue
  25. import stackless
  26. import log
  27. import uthread
  28. import weakref
  29. import sys
  30. import cPickle
  31. import binascii
  32. import copy
  33. import traceback
  34.  
  35. def FixString(s):
  36.     return str(s).replace('%','%%')
  37.     #return string.replace(str(s), '%', '%%')
  38.  
  39.  
  40. # Service state
  41. SERVICE_STOPPED = 1         # The service is not running.
  42. SERVICE_START_PENDING = 2   # The service is starting.
  43. SERVICE_STOP_PENDING = 3    # The service is stopping.
  44. SERVICE_RUNNING = 4         # The service is running.
  45. SERVICE_CONTINUE_PENDING =5 # The service continue is pending.
  46. SERVICE_PAUSE_PENDING = 6   # The service pause is pending.
  47. SERVICE_PAUSED = 7          # The service is paused.
  48.  
  49. # Service type
  50. SERVICETYPE_NORMAL = 1              # Default
  51. SERVICETYPE_BUILTIN = 2             # Means that the service wants to go into __builtin__
  52. SERVICETYPE_EXPORT_CONSTANTS = 4    # Indicates that this service only exports primitive constants
  53.  
  54. # Service control codes
  55. SERVICE_CONTROL_STOP = 1        # Requests the service to stop.
  56. SERVICE_CONTROL_PAUSE = 2       # Requests the service to pause.
  57. SERVICE_CONTROL_CONTINUE = 3    # Requests the paused service to resume.
  58. SERVICE_CONTROL_INTERROGATE = 4 # Requests the service to update immediately its current status information to the service control manager.
  59. SERVICE_CONTROL_SHUTDOWN = 5    # Requests the service to perform cleanup tasks, because the system is shutting down.
  60.  
  61. SERVICE_CHECK_NONE = 0      # Indicates that this service doesn't care about security
  62. SERVICE_CHECK_CALL = 1          # Indicates that this service prefers that a security check is made every call
  63. SERVICE_CHECK_INIT = 2      # Indicates that this service prefers that a security check is made when a connection is made
  64.  
  65. SERVICE_WANT_SESSIONS    = 1    # Indicates that this service wants sessions
  66.  
  67. ROLE_LOGIN              = 0x00000001
  68. ROLE_PLAYER             = 0x00000002
  69. ROLE_NPC                = 0x00000004
  70. ROLE_GML                = 0x00000008  ## Game Master Low
  71. ROLE_GMH                = 0x00000010  ## Game Master High
  72. ROLE_ADMIN              = 0x00000020  ## Administrator
  73. ROLE_SERVICE            = 0x00000040
  74. ROLE_HTTP               = 0x00000080  ## controls whether the session can access the server through http protocol
  75. ROLE_PETITIONEE         = 0x00000100  ## role for volunteers, enables a user to claim and resolve petitions
  76. ROLE_GDL                = 0x00000200  ## role for game design activity, modify messages, etc.
  77. ROLE_GDH                = 0x00000400  ## role for game design activity, modify type attributes, constant values etc.
  78. ROLE_CENTURION          = 0x00000800  ## role for senior volunteers, enables a user to claim and resolve petitions, escalate to GM and do bans
  79. ROLE_WORLDMOD           = 0x00001000  ## role for world modelling activity
  80. ROLE_QA                 = 0x00002000  ## role for quality assurance
  81. ROLE_EBS                = 0x00004000  ## role for eve backend system
  82. ROLE_CPUKILLER          = 0x00008000  ## role for ESP pages not allowed in LONDON
  83. ROLE_PROGRAMMER         = 0x00010000  ## role for executing Python code in the context of the server
  84. ROLE_REMOTESERVICE      = 0x00020000  ## Services
  85. ROLE_LEGIONEER          = 0x00040000  ## role for junior volunteers
  86. ROLE_CHTOPERATOR        = 0x00080000  ## Operator of a given chat channel
  87. ROLE_CHTMODERATOR       = 0x00100000  ## Moderator of a given chat channel
  88. ROLE_CHTCREATOR         = 0x00200000  ## Creator of a given chat channel
  89. ROLE_HEALSELF           = 0x00400000  ## /heal on yourself
  90. ROLE_HEALOTHERS         = 0x00800000  ## /heal on others
  91. ROLE_NEWSREPORTER       = 0x01000000  ## Forum and in-game news reporting role.
  92. ROLE_NEWSMODERATOR      = 0x02000000  ## Forum and in-game news moderator role.
  93. ROLE_NEWSADDRUMORS      = 0x04000000
  94. ROLE_GMSUPER            = 0x08000000  ## Game Master Super
  95. ROLE_N00BIE             = 0x10000000  ## Newbie dude
  96. ROLE_ACCOUNTMANAGEMENT  = 0x20000000  ## May manage user accounts.
  97. ROLE_ANY                = 0xFFFFFFFF
  98.  
  99. ROLEMASK_ELEVATEDPLAYER = (ROLE_ANY&~(ROLE_LOGIN|ROLE_PLAYER|ROLE_N00BIE))
  100.  
  101.  
  102. PRE_NONE        = 0
  103. PRE_AUTH        = 1
  104. PRE_HASCHAR     = 2
  105. PRE_HASSHIP     = 4
  106. PRE_INSTATION   = 8
  107. PRE_INFLIGHT    = 16
  108.  
  109. exports = {}
  110.  
  111. for i in locals().items():
  112. #    print i
  113.     if type(i[1]) == type(0):
  114.         exports ["service." + i[0] ] = i[1]
  115.  
  116.  
  117. # ---------------------------------------------------------------------------------------
  118. # used by PreCall_Lock for global and class level locks.
  119. globalAndClassLocks = {}
  120.  
  121. # ---------------------------------------------------------------------------------------
  122. # used to track ongoing calls
  123. callWrapperID   = 0
  124. allCallWrappers = weakref.WeakValueDictionary({})
  125. def GetCallWrappers():
  126.     return allCallWrappers
  127. exports["service.GetCallWrappers"] = GetCallWrappers
  128.  
  129. # ---------------------------------------------------------------------------------------
  130. class CachedResult:
  131.     def __init__(self, result):
  132.         self.result = result
  133.  
  134. #----------------------------------------------------------------------------------------
  135. class FastCallWrapper:
  136.     '''
  137.         A call wrapper that does the bare minimum required to keep things rolling, session
  138.         masquerading and call timing.
  139.     '''
  140.  
  141.     # -----------------------------------------------------------------------------------
  142.     def __init__(self,session,object,method,parent):
  143.         self.__session__  = session
  144.         self.__method__   = method
  145.         self.__callable__ = object
  146.         self.__parent__   = parent
  147.  
  148.     # -----------------------------------------------------------------------------------
  149.     def __call__(self,*args,**keywords):
  150.         mask = self.__session__.Masquerade( { 'base.caller': weakref.ref(self.__parent__)} )
  151.         try:
  152.             return apply(getattr(self.__callable__,self.__method__), args, keywords)
  153.         finally:
  154.             mask.UnMask()
  155.             self.__dict__.clear()
  156. exports["service.FastCallWrapper"] = FastCallWrapper
  157.  
  158. # ---------------------------------------------------------------------------------------
  159. class CallWrapper:
  160.     '''
  161.         A rather cool call wrapper with loads of default functionality and extension capabilities.
  162.  
  163.         The __precall__ and __postcall__ class or instance attributes of the wrapper dictates
  164.         extra call handling functionality.
  165.  
  166.         The call order is from left to right, both in pre and postcall.
  167.  
  168.         Each entry in the precall/postcall lists can be a:
  169.             1.  string, naming the method to call
  170.             2.  lambda, function or method, identifying the function to call
  171.             3.  dict, containing both the function name (1 or 2 above) under the key 'function', and any extra
  172.                 info you wish to send into the call handler's extra keyword params.
  173.     '''
  174.     if boot.role=='client':
  175.         __precall__         = [ "PreCall_IsExportedCall", "PreCall_CheckPreArgs", "PreCall_CachedMethodCall" ] # "PreCall_StartCallTimer",
  176.     else:
  177.         __precall__         = [ "PreCall_IsExportedCall", "PreCall_RoleCheck", "PreCall_CheckPreArgs", "PreCall_CachedMethodCall", "PreCall_Lock"  ] # "PreCall_StartCallTimer",
  178.     __postcall__        = [ "PostCall_CachedMethodCall", "PostCall_LogCompletedMethodCall", "PostCall_UnLock"] # "PostCall_StopCallTimer"
  179.  
  180.     # -----------------------------------------------------------------------------------
  181.     def __init__(self,session,object,method,parent,logname=None):
  182.         '''
  183.             Performs the call wrapper initialization.
  184.  
  185.             session             is the calling session.
  186.             object              is the actual object being called
  187.             method              is the method being called
  188.             parent              is the parent to the call wrap (a service or object
  189.                                 connection, probably).
  190.             logname             is the name of object, in loggable form
  191.         '''
  192.         if logname is None:
  193.             try:
  194.                 if hasattr(object, "__guid__"):
  195.                     logname = object.__guid__
  196.                     s = logname.split('.')
  197.                     if len(s)>1:
  198.                         logname = s[1]
  199.                 else:
  200.                     logname = object.__class__.__name__
  201.             except:
  202.                 logname = "CrappyClass"
  203.         self.__logname__            = logname
  204.         self.__session__            = session
  205.         self.__method__             = method
  206.         if method.endswith("_Ex"):
  207.             self.__method_without_Ex__  = method[:-3]
  208.         else:
  209.             self.__method_without_Ex__  = method
  210.         self.__callable__           = object
  211.         self.__parent__             = parent
  212.         self.__metadata__           = getattr(self.__callable__,"__exportedcalls__",{}).get(self.__method_without_Ex__,[])
  213.         self.__thread__  = stackless.getcurrent()
  214.  
  215.         global allCallWrappers
  216.         global callWrapperID
  217.         callWrapperID += 1
  218.         allCallWrappers[callWrapperID] = self
  219.     # -----------------------------------------------------------------------------------
  220.     def __call__(self,*args,**keywords):
  221.         '''
  222.             Performs the actual method call, including all automagical stuff in the pre-
  223.             and post-call list.
  224.  
  225.         '''
  226.         from base import CallTimer
  227.         from objectCaching import CacheOK
  228.         t = CallTimer(self.__logname__ + "::" + self.__method_without_Ex__)
  229.         try:
  230.             self.__arguments__  = args
  231.             mask = self.__session__.Masquerade( { 'base.caller': weakref.ref(self.__parent__)} )
  232.             try:
  233.                 cookies   = {}
  234.                 result    = None
  235.                 exctype   = None
  236.                 exc       = None
  237.                 tb        = None
  238.                 try:
  239.                     try:
  240.                         for method in self.__precall__:
  241.                             if type(self.__metadata__)==types.DictType:
  242.                                 extra  = self.__metadata__.get("callhandlerargs",{})
  243.                             else:
  244.                                 extra  = {}
  245.                             cookieName = method
  246.                             if type(method)==types.StringType:
  247.                                 method = getattr(self.__callable__,method,getattr(self,method,None))
  248.                             cookies[cookieName] = apply(method, (self.__method__, args, keywords,), extra)
  249.                     except CacheOK, e:
  250.                         result = e
  251.                     except CachedResult, e:
  252.                         result = e
  253.                     except Exception, e:
  254.                         exctype, exc, tb = sys.exc_info()
  255.                         result = e
  256.                     except:
  257.                         StackTrace()
  258.                         exctype, exc, tb = sys.exc_info()
  259.                         result = RuntimeError("SomethingHorribleHappenedDuringPreCall")
  260.  
  261.                     try:
  262.                         if result is None:
  263.                             args2 = args
  264.  
  265.                             if self.__method_without_Ex__==self.__method__:
  266.                                 preargs = []
  267.                                 if type(self.__metadata__)==types.DictType:
  268.                                     if "preargs" in self.__metadata__:
  269.                                         preargs = self.__metadata__["preargs"]
  270.                                 elif len(self.__metadata__)>1:
  271.                                     preargs = self.__metadata__[1:]
  272.                                 if preargs:
  273.                                     args2 = (len(preargs) * [None]) + list(args)
  274.                                     for i in range(len(preargs)):
  275.                                         args2[i] = getattr(session,preargs[i]) # Don't bother with a default value.  Preargs already checked.
  276.  
  277.                             result = apply(getattr(self.__callable__,self.__method_without_Ex__), args2, keywords)
  278.                     except UserError, e:
  279.                         exctype, exc, tb = sys.exc_info()
  280.                         result = e
  281.                     except Exception, e:
  282.                         exctype, exc, tb = sys.exc_info()
  283.                         result = e
  284.                     except:
  285.                         StackTrace(text = "something horrible happened during call")
  286.                         exctype, exc, tb = sys.exc_info()
  287.                         result = RuntimeError("SomethingHorribleHappenedDuringCall")
  288.  
  289.                     for method in self.__postcall__:
  290.                         try:
  291.                             if type(self.__metadata__)==types.DictType:
  292.                                 extra  = self.__metadata__.get("callhandlerargs",{})
  293.                             else:
  294.                                 extra  = {}
  295.                             if type(method)==types.StringType:
  296.                                 method = getattr(self.__callable__,method,getattr(self,method,None))
  297.                             apply(method, (cookies, result, self.__method__, args, keywords,), extra)
  298.                         except CacheOK, e:
  299.                             result = e
  300.                         except CachedResult, e:
  301.                             result = e
  302.                         except:
  303.                             StackTrace()
  304.  
  305.                     if result is not None:
  306.                         if isinstance(result, Exception):
  307.                             raise result, None, tb
  308.                         elif isinstance(result,CachedResult):
  309.                             return result.result
  310.                 finally:
  311.                     # IMPORTANT:  If you don't nullify the output of sys.exc_info(), you leak a lot.
  312.                     exctype = None
  313.                     exc     = None
  314.                     tb      = None
  315.             finally:
  316.                 mask.UnMask()
  317.                 self.__dict__.clear()
  318.  
  319.             return result
  320.         finally:
  321.             t.Done()
  322.  
  323.     # -----------------------------------------------------------------------------------
  324.     def PreCall_StartCallTimer(self, method, args, keywords, **mykeywords):
  325.         '''
  326.             Starts a call timer for this call
  327.         '''
  328.         from base import CallTimer
  329.         return CallTimer(self.__logname__ + "::" + self.__method_without_Ex__)
  330.     # -----------------------------------------------------------------------------------
  331.     def PostCall_StopCallTimer(self, cookies, result, method, args, keywords, **mykeywords):
  332.         '''
  333.             Stops a call timer for this call
  334.         '''
  335.         sct = cookies.get("PreCall_StartCallTimer",None)
  336.         if sct is not None:
  337.             sct.Done()
  338.  
  339.     # -----------------------------------------------------------------------------------
  340.     def PreCall_IsExportedCall(self, method, args, keywords, **mykeywords):
  341.         '''
  342.             Verifies that the call being made is available to the general public, or
  343.             subsets thereof.
  344.         '''
  345.         # can use method instead of method without ex, since services don't care whether or
  346.         # not a method is exported.
  347.         if (method not in self.__callable__.__exportedcalls__) and (method not in ("MachoResolve","MachoBindObject","MachoResolveObject",)):
  348.             if not ((self.__method__ != self.__method_without_Ex__) and (self.__method_without_Ex__ in self.__callable__.__exportedcalls__)):
  349.                 raise RuntimeError("In %s, method %s is not exported" % (self.__logname__, self.__method_without_Ex__))
  350.  
  351.     # -----------------------------------------------------------------------------------
  352.     def PreCall_RoleCheck(self, method, args, keywords, **mykeywords):
  353.         '''
  354.             Verifies that the calling session is allowed to call this method with regards
  355.             to role restrictions.
  356.         '''
  357.         if not (session.role&ROLE_SERVICE):
  358.             if type(self.__metadata__)==types.DictType:
  359.                 role = self.__metadata__.get("role",ROLE_SERVICE)
  360.             elif len(self.__metadata__):
  361.                 role = self.__metadata__[0]
  362.             elif method in ("MachoResolve","MachoBindObject","MachoResolveObject",):
  363.                 role = ROLE_ANY
  364.             else:
  365.                 role = ROLE_SERVICE
  366.  
  367.             if not (role&session.role):
  368.                 session.LogSessionError("Called %s::%s, which requires role 0x%x, which the user doesn't have"%(self.__logname__,method, role))
  369.                 raise RuntimeError("RoleNotAssigned", "%s::%s requires role 0x%x, which the user doesn't have.  Calling session: %s"%(self.__logname__,method, role, str(session)))
  370.  
  371.     # -----------------------------------------------------------------------------------
  372.     def PreCall_CheckPreArgs(self, method, args, keywords, **mykeywords):
  373.         '''
  374.             Checks that all the specified preargs are indeed available and have legal
  375.             values.
  376.         '''
  377.         noImplicits = (session.role&ROLE_SERVICE and method.endswith('_Ex'))
  378.         if type(self.__metadata__)==types.DictType:
  379.             preargs = self.__metadata__.get("preargs",[])
  380.         elif len(self.__metadata__):
  381.             preargs = self.__metadata__[1:]
  382.         else:
  383.             preargs = []
  384.  
  385.         for each in preargs:
  386.             if noImplicits and each not in ["sid", "userid"]:
  387.                 continue
  388.             prearg = getattr(session,each)
  389.             if prearg is None:
  390.                 session.LogSessionError("A required parameter exception occurred while the user was calling %s::%s.  The missing parameter was %s"%(self.__logname__,self.__method_without_Ex__,each))
  391.                 raise RuntimeError("RequiredParameterException", "%s::%s requires parameter %s, which the session doesn't have.  Calling session: %s"%(self.__logname__,self.__method_without_Ex__, each, str(session)))
  392.  
  393.     # -----------------------------------------------------------------------------------
  394.     def PostCall_LogCompletedMethodCall(self, cookies, result, method, args, keywords, **mykeywords):
  395.         '''
  396.             Logs a method call that has been completed, successfully or not
  397.         '''
  398.         logChannel = getattr(self.__callable__,"logChannel",log.methodcalls)
  399.         if logChannel.IsOpen(128):
  400.             if isinstance(result,Exception):
  401.                 eorr = ", EXCEPTION="
  402.             else:
  403.                 eorr = ", retval="
  404.             if keywords:
  405.                 logwhat = [ self.__logname__,"::",method," args=",args,", keywords={",keywords,"}",eorr,result ]
  406.             else:
  407.                 logwhat = [ self.__logname__,"::",method," args=",args,eorr,result ]
  408.             t = stackless.getcurrent()
  409.             if hasattr(t,"localStorage"):
  410.                 timer = t.PushTimer(self.__logname__ + "::LogCompletedMethodCall")
  411.             try:
  412.                 try:
  413.                     logChannel.Log(''.join(map(str, logwhat)), 128, 1)
  414.                 except TypeError:
  415.                     logChannel.Log( '[X]'.join(map(str, logwhat)).replace('\0','\\0'), 128, 1)
  416.             finally:
  417.                 if hasattr(t,"localStorage"):
  418.                     t.PopTimer(timer)
  419.  
  420.     # -----------------------------------------------------------------------------------
  421.     def PreCall_Lock(self, method, args, keywords, **mykeywords):
  422.         '''
  423.             Acquires a call lock for this group
  424.  
  425.             The PreCall_Lock function takes extra keywords to determine
  426.             the desired behaviour.
  427.  
  428.             key          values                  description
  429.             =======      ======================= =============================================
  430.             lock.lock    true or false           Whether or not this call uses a call lock.
  431.                                                  If true, then the rest of this section
  432.                                                  applies, if false, then this stuff is ignored.
  433.                                                  This is useful if f.ex. you place a lock on
  434.                                                  a class by default, and wish to disable it
  435.                                                  for a few calls.
  436.                                                  Defaults to true.
  437.  
  438.             lock.scope   connection, session,    The scope of the lock.  If 'connection' then
  439.                          instance, service,      the lock only applies to all calls coming
  440.                          class, server,          up this same object connection.  If 'session',
  441.                          usersession             then it applies to all calls coming up from
  442.                                                  the same session.  If 'instance', then it
  443.                                                  applies to all calls on this particular
  444.                                                  object instance.  If 'service', then it
  445.                                                  applies to all calls made to the service or
  446.                                                  any bound object it has, without regard to
  447.                                                  class or whatnot.  If 'class', then it applies
  448.                                                  to all calls made to this object class (as
  449.                                                  determined by the logname), and if 'server'
  450.                                                  then it applies to all calls on this server,
  451.                                                  without regard to object class, service, etc.
  452.                                                  'usersession' is same as 'session', but only
  453.                                                  user sessions trigger the lock.
  454.                                                  Defaults to usersession.
  455.  
  456.             lock.useargs true or false           If this is set true, then takes the arguments
  457.                                                  to the call into account in the locking
  458.                                                  scope.  In other words, 'instance'+'useargs'=1
  459.                                                  would mean all calls on this instance with
  460.                                                  these args to this method call.  Note that
  461.                                                  if you specify useargs, you have to have args
  462.                                                  that are usable as in a dict key.
  463.                                                  Defaults to false.
  464.  
  465.             lock.group   <string>                A unique string that identifies the lock within
  466.                                                  the given scope.  Note that the same string
  467.                                                  may be used f.ex. both in instance and server
  468.                                                  scopes without effecting each other.
  469.                                                  Defaults to the method name.
  470.  
  471.             lock.reaction block, inform, raise,  If 'block', then the call will block on a
  472.                           ignore                 semaphore waiting for a chance to continue.
  473.                                                  If 'inform', then the method will be called
  474.                                                  with added information in the keywords param
  475.                                                  of the method, so that the callee can determine
  476.                                                  that this has occurred, and react to it.  If
  477.                                                  'raise', then a default UserError will be
  478.                                                  raised, telling the user to sod off.  If 'ignore'
  479.                                                  then just returns None automagically.
  480.                                                  Defaults to block
  481.  
  482.             lock.raisemsg                        The message key to raise.  %(othermethod)s,
  483.                                                  %(thismethod)s, %(group)s, and %(scope)s are
  484.                                                  available as extra stuff.
  485.                                                  Defaults to "CallGroupLocked", which is a rather
  486.                                                  horrible system message.
  487.         '''
  488.         lock     = mykeywords.get('lock.lock',1)
  489.         if lock:
  490.             scope    = mykeywords.get('lock.scope','usersession')
  491.             if (scope=='usersession') and (self.__session__.role&ROLE_SERVICE):
  492.                 return
  493.             useargs  = mykeywords.get('lock.useargs',0)
  494.             group    = mykeywords.get('lock.group',method)
  495.             reaction = mykeywords.get('lock.reaction','block')
  496.  
  497.             if useargs:
  498.                 k = ("PreCall_Lock",group,args,)
  499.             else:
  500.                 k = ("PreCall_Lock",group,)
  501.  
  502.             if scope in ('session','usersession'):
  503.                 k = (k, "session", self.__session__.sid)
  504.                 l = self.__session__.GetSessionVariable( k, [uthread.Semaphore( k ), None] )
  505.             elif scope=='connection':
  506.                 k = (k, "connection", self.__parent__.__c2ooid__)
  507.                 l = self.__parent__.GetConnectionVariable( k )
  508.                 if l is None:
  509.                     l = [uthread.Semaphore( k ), None]
  510.                     self.__parent__.SetConnectionVariable( k, l )
  511.             elif scope=='instance':
  512.                 k = (k, "instance", self.__parent__.GetInstanceID())
  513.                 l = self.__parent__.GetInstanceVariable( k )
  514.                 if l is None:
  515.                     l = [uthread.Semaphore( k ), None]
  516.                     self.__parent__.SetInstanceVariable( k, l )
  517.             elif scope=='service':
  518.                 # if we're calling a service, then the behaviour is the same as for 'instance'
  519.                 # but if we're calling an object, it's not.
  520.                 if isinstance(self.__callable__,Service):
  521.                     k = (k, "service", self.__parent__.GetInstanceID())
  522.                     l = self.__parent__.GetInstanceVariable( k )
  523.                     if l is None:
  524.                         l = [uthread.Semaphore( k ), None]
  525.                         self.__parent__.SetInstanceVariable( k, l )
  526.                 else:
  527.                     srv = sm.StartService(self.__parent__.__serviceName__)
  528.                     k = (k, "service", srv.startedWhen)
  529.                     l = session.GetInstanceVariable(srv, k)
  530.                     if l is None:
  531.                         l = [uthread.Semaphore( k ), None]
  532.                         session.SetInstanceVariable( srv, k, l )
  533.             elif scope in ('class','global',):
  534.                 if scope=='class':
  535.                     try:
  536.                         k = (k, scope, self.__callable__.__class__.__name__)
  537.                     except:
  538.                         k = (k, scope, "logname", self.__logname__)
  539.                 l = globalAndClassLocks.get(k, None)
  540.                 if l is None:
  541.                     l = [uthread.Semaphore( k ), None]
  542.                     globalAndClassLocks[k] = l
  543.  
  544.             if l[0].count==0:
  545.                 if reaction=='raise':
  546.                     othermethod = '<unknown>'
  547.                     if l[1] is not None:
  548.                         othermethod = l[1][0]
  549.                     raise UserError(mykeywords.get("lock.raisemsg","CallGroupLocked"),{"thismethod":method, "group":group, "scope":scope, "othermethod":othermethod })
  550.                 elif reaction=='inform':
  551.                     keywords["reentrancy"] = l[1]
  552.                     return None
  553.                 elif reaction=='ignore':
  554.                     raise CachedResult(None)
  555.  
  556.             l[0].acquire()
  557.             l[1] = (method, args, keywords,)
  558.             return l
  559.  
  560.     # -----------------------------------------------------------------------------------
  561.     def PostCall_UnLock(self, cookies, result, method, args, keywords, **mykeywords):
  562.         l = cookies.get("PreCall_Lock",None)
  563.         if l is not None:
  564.             l[1] = None
  565.             l[0].release()
  566.  
  567.     # -----------------------------------------------------------------------------------
  568.     def PreCall_CachedMethodCall(self, method, args, keywords, **mykeywords):
  569.         '''
  570.             Performs the pre-call method call caching check, and strips out the machoVersion
  571.             flag, if present.
  572.         '''
  573.         machoVersion = keywords.get("machoVersion",None)
  574.         if machoVersion:
  575.             if len(keywords)==1:
  576.                 keywords.clear()
  577.             else:
  578.                 del keywords["machoVersion"]
  579.  
  580.         #return None
  581.  
  582.         cacheInfo = sm.StartService("objectCaching").PerformCachedMethodCall(self.__callable__, self.__logname__, self.__method_without_Ex__, args, machoVersion)
  583.         cachable, versionCheck, throwOK, cachedResultRecord, cacheKey, cacheDetails = cacheInfo
  584.  
  585.         if cachable and (not versionCheck):
  586.             if throwOK:
  587.                 from objectCaching import CacheOK
  588.                 raise CacheOK()
  589.             elif cachedResultRecord:
  590.                 if machoVersion:
  591.                     raise CachedResult(cachedResultRecord["rret"])
  592.                 else:
  593.                     raise CachedResult(cachedResultRecord["lret"])
  594.  
  595.         return (cacheInfo, machoVersion,)
  596.  
  597.     # -----------------------------------------------------------------------------------
  598.     def PostCall_CachedMethodCall(self, cookies, result, method, args, keywords, **mykeywords):
  599.         '''
  600.             Performs the post-call method call caching logic, resulting in the
  601.             call result being cached if appropriate.
  602.         '''
  603.         #return None
  604.  
  605.         cookie = cookies.get("PreCall_CachedMethodCall",None)
  606.         if cookie is not None:
  607.             if not isinstance(result,Exception):
  608.                 cacheInfo, machoVersion = cookie
  609.                 if cacheInfo[0]: # cachable
  610.                     srv  = sm.StartService("objectCaching")
  611.                     from objectCaching import CachedMethodCallResult, CacheOK
  612.                     from util import CachedObject
  613.                     cachable, versionCheck, throwOK, cachedResultRecord, cacheKey, cacheDetails = cacheInfo
  614.                     cmcr = CachedMethodCallResult(cacheKey, cacheDetails, result,)
  615.                     srv.CacheMethodCall(self.__logname__,self.__method_without_Ex__,args,cmcr)
  616.                     if (machoVersion is not None):
  617.                         if (machoVersion!=1) and (cmcr.version[1]==machoVersion[1]):
  618.                             srv.LogInfo("ObjectCaching ",self.__logname__,"::",self.__method_without_Ex__,"(",args,") is the correct version.  raising CacheOK")
  619.                             raise CacheOK()
  620.                         else:
  621.                             srv.LogInfo("ObjectCaching ",self.__logname__,"::",self.__method_without_Ex__,"(",args,") is not the correct version.  returning a cachable result.  caller=",machoVersion,", server=",cmcr.version)
  622.                         raise CachedResult(cmcr)
  623.                     else:
  624.                         srv.LogInfo("ObjectCaching ",self.__logname__,"::",self.__method_without_Ex__,"(",args,") the client didn't provide a macho version")
  625.  
  626. exports["service.CallWrapper"] = CallWrapper
  627.  
  628. # -----------------------------------------------------------------------------------
  629. class ServiceCallWrapper(CallWrapper):
  630.     '''
  631.         A call wrapper with the needs of services in mind.
  632.  
  633.         This call wrapper add's waiting for the service to run to it's precall checks.
  634.     '''
  635.     if boot.role=='client':
  636.         __precall__         = [ "PreCall_WaitForRunningState", "PreCall_IsExportedCall", "PreCall_CheckPreArgs", "PreCall_CachedMethodCall" ] #"PreCall_StartCallTimer",
  637.     else:
  638.         __precall__         = [ "PreCall_WaitForRunningState", "PreCall_IsExportedCall", "PreCall_RoleCheck", "PreCall_CheckPreArgs", "PreCall_CachedMethodCall" ] # "PreCall_StartCallTimer",
  639.  
  640.     # -----------------------------------------------------------------------------------
  641.     def PreCall_WaitForRunningState(self, method, args, keywords, **mykeywords):
  642.         '''
  643.             Wait until the service achieves running state, or stops.  Doesn't
  644.             actually try to start the service.
  645.         '''
  646.         i = 0
  647.         while self.__callable__.state != SERVICE_RUNNING:
  648.             if self.__callable__.state == SERVICE_STOPPED:
  649.                 raise RuntimeError("ServiceStopped", self.__callable__)
  650.             blue.pyos.synchro.Sleep(100)
  651.             if ((i % 600)==0) and (i>0):
  652.                 self.__callable__.LogWarn("PreCallHandler:  ",method, args, keywords, " has been sleeping for a long time waiting for ",self.__logname__," to either get to running state, or to stopped state")
  653. exports["service.ServiceCallWrapper"] = ServiceCallWrapper
  654.  
  655. # -----------------------------------------------------------------------------------
  656. """
  657.  
  658.     Using vanilla call wrappers for the moment for object calls.
  659.  
  660. class ObjectCallWrapper(CallWrapper):
  661.     '''
  662.         A call wrapper with the needs of bound objects in mind.
  663.  
  664.         This call wrapper does a magical rebind check, giving the callee an opportunity
  665.         to bounce the caller off to another node or the like.
  666.     '''
  667.     if boot.role=='client':
  668.         __precall__         = [ "PreCall_StartCallTimer", "PreCall_ReResolveObject", "PreCall_UpdateMoniker", "PreCall_IsExportedCall", "PreCall_CheckPreArgs", "PreCall_CachedMethodCall" ]
  669.     else:
  670.         __precall__         = [ "PreCall_StartCallTimer", "PreCall_ReResolveObject", "PreCall_UpdateMoniker", "PreCall_IsExportedCall", "PreCall_RoleCheck", "PreCall_CheckPreArgs", "PreCall_CachedMethodCall" ]
  671.  
  672.     # -----------------------------------------------------------------------------------
  673.     def PreCall_ReResolveObject(self, method, args, keywords, **mykeywords):
  674.         '''
  675.             Double-checks that the bound object still resides on this node, and gives MachoResolveObject
  676.             an opportunity to raise an UpdateMoniker or otherwise tell the caller to bugger off.
  677.         '''
  678.         if (self.__parent__.__serviceName__ is not None) and (self.__parent__.__bindParams__ is not None) and (self.__parent__.__redirectObject__ is None):
  679.             nodeID = sm.StartService(self.__parent__.__serviceName__).MachoResolveObject(self.__parent__.__bindParams__,self.__GetJustQuery())
  680.             if nodeID != sm.StartService("machoNet").GetNodeID():
  681.                 self.__parent__.__redirectObject__ = (self.__parent__.__serviceName__,self.__parent__.__bindParams__,)
  682.  
  683.     # -----------------------------------------------------------------------------------
  684.     def PreCall_UpdateMoniker(self, method, args, keywords, **mykeywords):
  685.         '''
  686.             Handles redirecting the call if we were previously directed to do so.
  687.         '''
  688.         if self.__parent__.__redirectObject__ is not None:
  689.             nodeID = sm.StartService(self.__parent__.__serviceName__).MachoResolveObject(self.__parent__.__redirectObject__[1],self.__GetJustQuery())
  690.             from util import UpdateMoniker
  691.             raise UpdateMoniker(self.__parent__.__redirectObject__[0], self.__parent__.__redirectObject__[1], nodeID)
  692.  
  693.     # -----------------------------------------------------------------------------------
  694.     def __GetJustQuery(self):
  695.         if stackless.getcurrent().block_trap or stackless.getcurrent().is_main:
  696.             return 3
  697.         else:
  698.             return 0
  699.  
  700. exports["service.ObjectCallWrapper"] = ObjectCallWrapper
  701. """
  702. exports["service.ObjectCallWrapper"] = CallWrapper
  703.  
  704.  
  705. #########################################################################################
  706. globalMiniTraceStats = {}
  707.  
  708. class MiniTraceCallWrapper:
  709.  
  710.     def __init__(self, service, func, funcName, depth):
  711.         self.service    = weakref.proxy(service)
  712.         self.func       = func
  713.         self.funcName   = funcName
  714.         self.depth      = depth
  715.  
  716.     def __call__(self, *args, **kw):
  717.         if self.service.logChannel.IsOpen(1):
  718.             stats = globalMiniTraceStats[(self.service.__guid__,self.funcName)]
  719.             f = stackless.getcurrent().frame
  720.             k0 = None
  721.             depth = 0
  722.             while f and ((self.depth==-1 and k0 is None) or (depth<self.depth)):
  723.                 depth += 1
  724.                 f = f.f_back
  725.                 if f:
  726.                     k = f.f_code.co_filename, f.f_code.co_name, traceback.f_lineno(f)
  727.                     if (k0 is None) and (k[1]!="__call__"):
  728.                         k0 = k
  729.                     stats[k] = stats.get(k,0) + 1
  730.             self.service.LogInfo(self.funcName,args,kw," is being called by ",k0[0]," in line ",k0[2]," of ",k0[0])
  731.             return apply( self.func, args, kw)
  732.         else:
  733.             return apply( self.func, args, kw)
  734.  
  735. exports["service.MiniTraceStats"] = globalMiniTraceStats
  736.  
  737. #########################################################################################
  738. ## //////////////////////////////////////////////////////////////////////////////////////
  739. ## //
  740. ## //   Class Service - Service
  741. ## //
  742. ## //   The base for all service in the eve cluster
  743. ## //
  744. ## //
  745.  
  746.  
  747. class Service:
  748.  
  749.     __dependencies__    = []
  750.     __required__        = ["Sessions"]
  751.     __sessionparams__   = []
  752.     __servicetype__     = SERVICETYPE_NORMAL
  753.     __name__            = "service"
  754.     __displayname__     = "Basic Service"
  755.     __persistvars__     = []
  756.     __nonpersistvars__  = []
  757.     __exportedcalls__   = {}
  758.     __notifyevents__    = []
  759.     __configvalues__    = {}
  760.     __counters__        = {}
  761.     __machocacheobjects__ = 1
  762.     __machoresolve__    = None # None, clustersingleton, station, solarsystem, or location
  763.  
  764.     # -----------------------------------------------------------------------------------
  765.     #  Service - Constructor
  766.     # -----------------------------------------------------------------------------------
  767.     def __init__(self):
  768.         self.boundObjects = {}
  769.         self.serviceLocks = {}
  770.         self.startedWhen  = blue.os.GetTime(1)
  771.  
  772.         ## Initialize the log channel for the service
  773.         guidParts = self.__guid__.split('.')
  774.         self.__logname__ = guidParts[1]
  775.         self.logChannel = blue.pyos.CreateLogChannel(guidParts[0], guidParts[1])
  776.  
  777.         self.InitService()
  778.  
  779.         if len(self.__counters__) and not ("counter" in self.__dependencies__):
  780.             self.__dependencies__.append("counter")
  781.  
  782.         self.logContexts = {}
  783.         for each in ('Info','Warn', 'Error', 'Perf','Fatal','Counter',):
  784.             ##self.logContexts[each] = self.__logname__ + "::Log" + each
  785.             self.logContexts[each] = "Logging::" + each
  786.  
  787.     # -----------------------------------------------------------------------------------
  788.     def MachoResolve(self, sess):
  789.         '''
  790.             By default, all services run on all machines, except clustersingletons,
  791.             station and solarsystem based services.
  792.         '''
  793.         if self.__machoresolve__ is not None:
  794.             mn = sm.services["machoNet"]
  795.             if   self.__machoresolve__ == "clustersingleton":
  796.                 return mn.GetNodeFromAddress(self.__logname__,0)
  797.  
  798.             if not (sess.role&ROLE_SERVICE):
  799.                 # Remote service calls are performed on the correct machine as a prerequisite.
  800.  
  801.                 if self.__machoresolve__ == "station":
  802.                     if not sess.stationid:
  803.                         return "You must be located at a station to use this service"
  804.                     return mn.GetNodeFromAddress("station",sess.stationid)
  805.  
  806.                 elif self.__machoresolve__ == "solarsystem":
  807.                     if not sess.solarsystemid:
  808.                         return "You must be located in a solar system to use this service"
  809.                     return mn.GetNodeFromAddress("beyonce",sess.solarsystemid)
  810.  
  811.                 elif self.__machoresolve__ in ("location","locationPreferred"):
  812.                     if not sess.locationid:
  813.                         if self.__machoresolve__=="locationPreferred":
  814.                             return None
  815.                         return "You must be located in a solar system or at station to use this service"
  816.                     elif sess.solarsystemid:
  817.                         return mn.GetNodeFromAddress("beyonce",sess.solarsystemid)
  818.                     else:
  819.                         return mn.GetNodeFromAddress("station",sess.locationid)
  820.  
  821.                 else:
  822.                     raise RuntimeError("This service is crap (%s)"%self.__logname__)
  823.  
  824.         return None
  825.  
  826.     # -----------------------------------------------------------------------------------
  827.     def LockService(self,lockID):
  828.         '''
  829.             For those operations which support and/or require service locks, here's the
  830.             locking function for ya.
  831.         '''
  832.         if not lockID in self.serviceLocks:
  833.             lock = uthread.CriticalSection(("service::serviceLock",self.__logname__,lockID,))
  834.             self.serviceLocks[lockID] = lock
  835.         else:
  836.             lock = self.serviceLocks[lockID]
  837.         lock.acquire()
  838.         return lock
  839.  
  840.     # -----------------------------------------------------------------------------------
  841.     def UnLockService(self,lockID,lock):
  842.         '''
  843.             For those operations which support and/or require service locks, here's the
  844.             unlocking function for ya.
  845.         '''
  846.         if self.serviceLocks.get(lockID,None) != lock:
  847.             if lock is not None:
  848.                 self.LogError("Service lock locked up.  This is the lock in servicelocks=", str(self.serviceLocks.get(lockID,None)), ", and this is the lock we got returned=",str(lock))
  849.                 lock.release()
  850.             else:
  851.                 self.LogError("Service lock locked up big time.  This is the lock in servicelocks=", str(self.serviceLocks.get(lockID,None)), ", and this is the lock we got returned=",str(lock))
  852.         else:
  853.             self.serviceLocks[lockID].release()
  854.         if lockID in self.serviceLocks and self.serviceLocks[lockID].IsCool():
  855.             del self.serviceLocks[lockID]
  856.  
  857.     # -----------------------------------------------------------------------------------
  858.     def StartMiniTrace(self,funcName, depth = 16):
  859.         '''
  860.             Wraps this service's function 'funcName' in a mini-trace call wrapper, that
  861.             helps find who the farg is calling the function so often...
  862.         '''
  863.         if funcName not in globalMiniTraceStats:
  864.             globalMiniTraceStats[(self.__guid__,funcName)] = {}
  865.             setattr(self, funcName, MiniTraceCallWrapper(self,getattr(self,funcName),funcName, depth))
  866.  
  867.     # -----------------------------------------------------------------------------------
  868.     def StopMiniTrace(self,funcName, depth = 16):
  869.         '''
  870.             Wraps this service's function 'funcName' in a mini-trace call wrapper, that
  871.             helps find who the farg is calling the function so often...
  872.         '''
  873.         if funcName not in globalMiniTraceStats:
  874.             del globalMiniTraceStats[(self.__guid__,funcName)]
  875.             setattr(self, funcName, getattr(self,funcName).func)
  876.  
  877.     # -----------------------------------------------------------------------------------
  878.     def GetDependants(self):
  879.         dependants = []
  880.         guidParts  = self.__guid__.split('.')
  881.         if len(guidParts):
  882.             myName    = guidParts[1]
  883.         else:
  884.             myName    = None
  885.  
  886.         for i in sm.services:
  887.             if myName in sm.services[i].__dependencies__:
  888.                 guidParts = sm.services[i].__guid__.split('.')
  889.                 if len(guidParts):
  890.                     dependants.append(guidParts[1])
  891.                 else:
  892.                     dependants.append(sm.services[i].__guid__)
  893.         return dependants
  894.  
  895.     # -----------------------------------------------------------------------------------
  896.     def GetDeepDependants(self,theArr):
  897.         newbies = []
  898.         for each in self.GetDependants():
  899.             if not (each in theArr):
  900.                 newbies.append( each )
  901.                 theArr.append ( each )
  902.         for each in newbies:
  903.             sm.services[each].GetDeepDependants(theArr)
  904.  
  905.     # -----------------------------------------------------------------------------------
  906.     def GetDeepDependencies(self,theArr):
  907.         newbies = []
  908.         for each in self.__dependencies__:
  909.             if not (each in theArr):
  910.                 newbies.append( each )
  911.                 theArr.append ( each )
  912.         for each in newbies:
  913.             sm.services[each].GetDeepDependencies(theArr)
  914.  
  915.     # -----------------------------------------------------------------------------------
  916.     #  InitService
  917.     # -----------------------------------------------------------------------------------
  918.     def InitService(self):
  919.         self.state = SERVICE_STOPPED
  920.  
  921.     # __getattr__ operator - Providing config values as well
  922.     # -----------------------------------------------------------------------------------
  923.     def __getattr__(self, key):
  924.         if key in self.__dict__:
  925.             return self.__dict__[key]
  926.         if key in self.__configvalues__:
  927.             daKey="%s.%s"%(self.__logname__,key,)
  928.             return prefs.GetValue(daKey, boot.GetValue(daKey,self.__configvalues__[key]))
  929.         elif key in self.__counters__:
  930.             self.__dict__[key] = self.counter.CreateCounter(key,self.__counters__[key])
  931.             return self.__dict__[key]
  932.         elif hasattr(self.__class__,key):
  933.             return getattr(self.__class__,key)
  934.         else:
  935.             raise AttributeError, key
  936.  
  937.     # __setattr__ operator - Modifying config values as well
  938.     # -----------------------------------------------------------------------------------
  939.     def __setattr__(self, key, value):
  940.         if key in self.__configvalues__:
  941.             prefs.SetValue("%s.%s"%(self.__logname__,key),value)
  942.             return
  943.  
  944.         self.__dict__[key]=value
  945.  
  946.     # -----------------------------------------------------------------------------------
  947.     #  ControlMessage
  948.     # -----------------------------------------------------------------------------------
  949.     def ControlMessage(self, control):
  950.         if control == SERVICE_CONTROL_STOP:
  951.             self.state = SERVICE_STOPPED
  952.         elif control == SERVICE_CONTROL_PAUSE:
  953.             self.state = SERVICE_PAUSED
  954.         elif control == SERVICE_CONTROL_CONTINUE:
  955.             self.state = SERVICE_RUNNING
  956.         elif control == SERVICE_CONTROL_INTERROGATE:
  957.             pass
  958.         elif control == SERVICE_CONTROL_SHUTDOWN:
  959.             self.state = SERVICE_STOPPED
  960.  
  961.     # -----------------------------------------------------------------------------------
  962.     def DudLogger(self, *args, **keywords):
  963.         pass
  964.  
  965.     # -----------------------------------------------------------------------------------
  966.     #  LogMethodCall
  967.     # -----------------------------------------------------------------------------------
  968.     def LogMethodCall(self, *args, **keywords):
  969.         if self.logChannel.IsOpen(128):
  970.             t = stackless.getcurrent()
  971.             if hasattr(t,"localStorage"):
  972.                 timer = t.PushTimer(self.logContexts["Info"])
  973.             try:
  974.                 try:
  975.                     if (len(args)==1):
  976.                         s = str(args[0])
  977.                     else:
  978.                         s = ' '.join(map(str, args))
  979.                     self.logChannel.Log(s, 128, 1)
  980.                 except TypeError:
  981.                     self.logChannel.Log( '[X]'.join(map(str, args)).replace('\0','\\0'), 128, 1)
  982.             finally:
  983.                 if hasattr(t,"localStorage"):
  984.                     t.PopTimer(timer)
  985.         else:
  986.             self.LogMethodCall = self.DudLogger
  987.  
  988.     # -----------------------------------------------------------------------------------
  989.     #  LogInfo
  990.     # -----------------------------------------------------------------------------------
  991.     def LogInfo(self, *args, **keywords):
  992.         if self.logChannel.IsOpen(1):
  993.             t = stackless.getcurrent()
  994.             if hasattr(t,"localStorage"):
  995.                 timer = t.PushTimer(self.logContexts["Info"])
  996.             try:
  997.                 try:
  998.                     if (len(args)==1):
  999.                         s = str(args[0])
  1000.                     else:
  1001.                         s = ' '.join(map(str, args))
  1002.  
  1003.                     for i in range(0,1012,253): # four lines max.
  1004.                         if i==0:
  1005.                             if len(s)<=255:
  1006.                                 x = s
  1007.                             else:
  1008.                                 x = s[:(i+253)]
  1009.                         else:
  1010.                             x = " - " + s[i:(i+253)]
  1011.                         self.logChannel.Log(x, 1, 1)
  1012.                         if (i+253)>=len(s):
  1013.                             break
  1014.                 except TypeError:
  1015.                     self.logChannel.Log( '[X]'.join(map(str, args)).replace('\0','\\0'), 1, 1)
  1016.             finally:
  1017.                 if hasattr(t,"localStorage"):
  1018.                     t.PopTimer(timer)
  1019.         else:
  1020.             self.LogInfo = self.DudLogger
  1021.  
  1022.     # -----------------------------------------------------------------------------------
  1023.     #  LogWarn
  1024.     # -----------------------------------------------------------------------------------
  1025.     def LogWarn(self, *args, **keywords):
  1026.         if self.logChannel.IsOpen(2) or (charsession and (not boot.role=='client')):
  1027.             t = stackless.getcurrent()
  1028.             if hasattr(t,"localStorage"):
  1029.                 timer = t.PushTimer(self.logContexts["Warn"])
  1030.             try:
  1031.                 try:
  1032.                     if (len(args)==1):
  1033.                         s = str(args[0])
  1034.                     else:
  1035.                         s = ' '.join(map(str, args))
  1036.  
  1037.                     for i in range(0,2530,253): # ten lines max.
  1038.                         if i==0:
  1039.                             if len(s)<=255:
  1040.                                 x = s
  1041.                             else:
  1042.                                 x = s[:(i+253)]
  1043.                         else:
  1044.                             x = " - " + s[i:(i+253)]
  1045.                         if self.logChannel.IsOpen(2):
  1046.                             self.logChannel.Log(x, 2, 1)
  1047.                         if (charsession and (not boot.role=='client')):
  1048.                             charsession.LogSessionHistory(x,None,1)
  1049.                         if (i+253)>=len(s):
  1050.                             break
  1051.                 except TypeError:
  1052.                     x = '[X]'.join(map(str, args)).replace('\0','\\0')
  1053.                     if self.logChannel.IsOpen(2):
  1054.                         self.logChannel.Log( x, 2, 1)
  1055.                     if (charsession and (not boot.role=='client')):
  1056.                         charsession.LogSessionHistory(x,None,1)
  1057.             finally:
  1058.                 if hasattr(t,"localStorage"):
  1059.                     t.PopTimer(timer)
  1060.         elif boot.role=='client':
  1061.             self.LogWarn = self.DudLogger
  1062.  
  1063.     # -----------------------------------------------------------------------------------
  1064.     #  LogError
  1065.     # -----------------------------------------------------------------------------------
  1066.     def LogError(self, *args, **keywords):
  1067.         if self.logChannel.IsOpen(4) or charsession:
  1068.             t = stackless.getcurrent()
  1069.             if hasattr(t,"localStorage"):
  1070.                 timer = t.PushTimer(self.logContexts["Error"])
  1071.             try:
  1072.                 try:
  1073.                     if (len(args)==1):
  1074.                         s = str(args[0])
  1075.                     else:
  1076.                         s = ' '.join(map(str, args))
  1077.  
  1078.                     for i in range(0,10120,253): # forty lines max.
  1079.                         if i==0:
  1080.                             if len(s)<=255:
  1081.                                 x = s
  1082.                             else:
  1083.                                 x = s[:(i+253)]
  1084.                         else:
  1085.                             x = " - " + s[i:(i+253)]
  1086.                         if self.logChannel.IsOpen(4):
  1087.                             self.logChannel.Log(x, 4, 1)
  1088.                         if charsession:
  1089.                             charsession.LogSessionHistory(x,None,1)
  1090.                         if (i+253)>=len(s):
  1091.                             break
  1092.                 except TypeError:
  1093.                     x = '[X]'.join(map(str, args)).replace('\0','\\0')
  1094.                     if self.logChannel.IsOpen(4):
  1095.                         self.logChannel.Log( x, 4, 1)
  1096.                     if charsession:
  1097.                         charsession.LogSessionHistory(x,None,1)
  1098.             finally:
  1099.                 if hasattr(t,"localStorage"):
  1100.                     t.PopTimer(timer)
  1101.         #else:
  1102.         #    self.LogError = self.DudLogger
  1103.  
  1104.     # -----------------------------------------------------------------------------------
  1105.     #  LogFatal
  1106.     # -----------------------------------------------------------------------------------
  1107.     def LogFatal(self, *args, **keywords):
  1108.         if self.logChannel.IsOpen(8) or charsession:
  1109.             t = stackless.getcurrent()
  1110.             if hasattr(t,"localStorage"):
  1111.                 timer = t.PushTimer(self.logContexts["Fatal"])
  1112.             try:
  1113.                 try:
  1114.                     if (len(args)==1):
  1115.                         s = str(args[0])
  1116.                     else:
  1117.                         s = ' '.join(map(str, args))
  1118.  
  1119.                     for i in range(0,10120,253): # forty lines max.
  1120.                         if i==0:
  1121.                             if len(s)<=255:
  1122.                                 x = s
  1123.                             else:
  1124.                                 x = s[:(i+253)]
  1125.                         else:
  1126.                             x = " - " + s[i:(i+253)]
  1127.                         if self.logChannel.IsOpen(8):
  1128.                             self.logChannel.Log(x, 8, 1)
  1129.                         if charsession:
  1130.                             charsession.LogSessionHistory(x,None,1)
  1131.                         if (i+253)>=len(s):
  1132.                             break
  1133.                 except TypeError:
  1134.                     x = '[X]'.join(map(str, args)).replace('\0','\\0')
  1135.                     if self.logChannel.IsOpen(8):
  1136.                         self.logChannel.Log( x, 8, 1)
  1137.                     if charsession:
  1138.                         charsession.LogSessionHistory(x,None,1)
  1139.             finally:
  1140.                 if hasattr(t,"localStorage"):
  1141.                     t.PopTimer(timer)
  1142.         #else:
  1143.         #    self.LogFatal = self.DudLogger
  1144.  
  1145.     # -----------------------------------------------------------------------------------
  1146.     #  LogPerf
  1147.     # -----------------------------------------------------------------------------------
  1148.     def LogPerf(self, *args, **keywords):
  1149.         if self.logChannel.IsOpen(32):
  1150.             t = stackless.getcurrent()
  1151.             if hasattr(t,"localStorage"):
  1152.                 timer = t.PushTimer(self.logContexts["Perf"])
  1153.             try:
  1154.                 try:
  1155.                     if (len(args)==1):
  1156.                         self.logChannel.Log( str(args[0]), 32, 1)
  1157.                     else:
  1158.                         self.logChannel.Log( ' '.join(map(str, args)), 32, 1)
  1159.                 except TypeError:
  1160.                     self.logChannel.Log( '[X]'.join(map(str, args)).replace('\0','\\0'), 32, 1)
  1161.             finally:
  1162.                 if hasattr(t,"localStorage"):
  1163.                     t.PopTimer(timer)
  1164.         else:
  1165.             self.LogPerf = self.DudLogger
  1166.  
  1167.     # -----------------------------------------------------------------------------------
  1168.     #  LogCounter
  1169.     # -----------------------------------------------------------------------------------
  1170.     def LogCounter(self, value):
  1171.         if self.logChannel.IsOpen(64):
  1172.             t = stackless.getcurrent()
  1173.             if hasattr(t,"localStorage"):
  1174.                 timer = t.PushTimer(self.logContexts["Counter"])
  1175.             try:
  1176.                 self.logChannel.LogCounter(value)
  1177.             finally:
  1178.                 if hasattr(t,"localStorage"):
  1179.                     t.PopTimer(timer)
  1180.         else:
  1181.             self.LogCounter = self.DudLogger
  1182.  
  1183.  
  1184.     # -----------------------------------------------------------------------------------
  1185.     #  Run
  1186.     # -----------------------------------------------------------------------------------
  1187.     def Run(self, memStream = None):
  1188.         Import("blue")
  1189.         self.LogInfo("Service: %s starting" % (self.__guid__,))
  1190.         self.boundObjects = {}
  1191.  
  1192.  
  1193.     # -----------------------------------------------------------------------------------
  1194.     #  Entering
  1195.     # -----------------------------------------------------------------------------------
  1196.     def Entering(self):
  1197.         pass
  1198.  
  1199.  
  1200.     # -----------------------------------------------------------------------------------
  1201.     #  Stop
  1202.     # -----------------------------------------------------------------------------------
  1203.     def Stop(self,memStream = None):
  1204.         for bo in self.boundObjects.values():
  1205.             for sess in bo.sessionConnections.values():
  1206.                 sess.DisconnectObject(bo)
  1207.         self.boundObjects = {}
  1208.  
  1209.     # -----------------------------------------------------------------------------------
  1210.     #  GetSessionState
  1211.     # -----------------------------------------------------------------------------------
  1212.     def GetSessionState(self, session_ = None):
  1213.         if session_ is not None:
  1214.             session = session_
  1215.  
  1216.         if session is None:
  1217.             raise RuntimeError("No session")
  1218.  
  1219.         return session.GetBag(self.__guid__)
  1220.  
  1221.     # -----------------------------------------------------------------------------------
  1222.     #  GetServiceType
  1223.     # -----------------------------------------------------------------------------------
  1224.     def GetServiceType(self):
  1225.  
  1226.         types = {
  1227.             SERVICETYPE_NORMAL              : "Normal",
  1228.             SERVICETYPE_BUILTIN             : "Built-in",
  1229.             SERVICETYPE_EXPORT_CONSTANTS    : "Constants",
  1230.         }
  1231.  
  1232.         return types[self.__servicetype__]
  1233.  
  1234.     # -----------------------------------------------------------------------------------
  1235.     #  GetServiceState
  1236.     # -----------------------------------------------------------------------------------
  1237.     def GetServiceState(self):
  1238.  
  1239.         states = {
  1240.             SERVICE_STOPPED             : "Stopped",
  1241.             SERVICE_START_PENDING       : "Start Pending",
  1242.             SERVICE_STOP_PENDING        : "Stop Pending",
  1243.             SERVICE_RUNNING             : "Running",
  1244.             SERVICE_CONTINUE_PENDING    : "Continue Pending",
  1245.             SERVICE_PAUSE_PENDING       : "Pause Pending",
  1246.             SERVICE_PAUSED              : "Paused",
  1247.         }
  1248.  
  1249.         return states[self.state]
  1250.  
  1251.     # -----------------------------------------------------------------------------------
  1252.     #  GetHtmlState
  1253.     # -----------------------------------------------------------------------------------
  1254.     def GetHtmlState(self, writer, session = None, request = None):
  1255.         import types
  1256.         import htmlwriter
  1257.         if writer is None:
  1258.             return "(no info available)"
  1259.         else:
  1260.             writer.Write(self.HTMLDumpProperties("Basic Service properties", "wpServiceProperties", self, session, request ))
  1261.  
  1262.             if len(self.__configvalues__):
  1263.                 hd = ["Key","Pretty Name","Value","Default Value", "Info"]
  1264.                 li = []
  1265.                 for each in self.__configvalues__.iterkeys():
  1266.                     prettyname = each
  1267.                     info = None
  1268.                     value = getattr(self,each)
  1269.                     if hasattr(self,"GetHtmlStateDetails"):
  1270.                         r = self.GetHtmlStateDetails(each,value,0)
  1271.                         if r:
  1272.                             prettyname, info = r[0], r[1]
  1273.                     if value != self.__configvalues__[each]:
  1274.                         value = "<b>%s</b>"%value
  1275.                     li.append( [each, prettyname, value, self.__configvalues__[each], info ] )
  1276.  
  1277.                 li.sort(lambda a, b: -(a[0].upper() < b[0].upper()))
  1278.                 writer.Write(htmlwriter.WebPart("Service Config Values", htmlwriter.OutlineTable(hd, li), "wpServiceConfigValues"))
  1279.  
  1280.             if len(self.__counters__):
  1281.                 hd = ["Key","Pretty Name","Type","Current Value","Description"]
  1282.                 li = []
  1283.                 for each in self.__counters__.iterkeys():
  1284.                     cname       = each
  1285.                     prettyname  = cname
  1286.                     ctype       = self.__counters__[each]
  1287.                     cvalue      = 0
  1288.                     if hasattr(self,each):
  1289.                         cvalue = getattr(self,each).Value()
  1290.                     info        = None
  1291.                     if hasattr(self,"GetHtmlStateDetails"):
  1292.                         r = self.GetHtmlStateDetails(each,cvalue,0)
  1293.                         if r:
  1294.                             prettyname, info = r[0], r[1]
  1295.                     li.append( [cname, prettyname, ctype, cvalue, info ] )
  1296.  
  1297.                 li.sort(lambda a, b: -(a[0].upper() < b[0].upper()))
  1298.                 writer.Write(htmlwriter.WebPart("Service Counters", htmlwriter.OutlineTable(hd, li), "wpServiceCounters"))
  1299.  
  1300.             writer.WriteH2("Service Member Variables")
  1301.             writer.Write(self.HTMLDumpGenericMembers(self, session, request))
  1302.  
  1303.             if hasattr(self,"boundObjects"):
  1304.                 if request:
  1305.                     objectDetail = request.QueryString("objectDetail")
  1306.                     if objectDetail is not None:
  1307.                         objectDetail = cPickle.loads( binascii.a2b_hex( objectDetail ) )
  1308.                 else:
  1309.                     objectDetail = None
  1310.                 object = self.boundObjects.get(objectDetail,None)
  1311.                 if object:
  1312.                     writer.WriteH2("Bound Object %s"%str(objectDetail))
  1313.                     if object.__doc__ is not None:
  1314.                         writer.Write(htmlwriter.Swing(object.__doc__).replace("\n", "<br>"))
  1315.                     writer.Write(self.HTMLDumpProperties("Properties of Bound Object %s"%str(objectDetail), "wpBoundObjectProperties %s"%str(objectDetail), object, session, request ))
  1316.                     writer.Write(self.HTMLDumpGenericMembers(object, session, request))
  1317.  
  1318.  
  1319.     def HTMLDumpGenericMembers(self, dumpWho, session, request):
  1320.         import htmlwriter
  1321.         if request:
  1322.             detail = request.QueryString("detail")
  1323.         else:
  1324.             detail = None
  1325.  
  1326.         def Str(v):
  1327.             import htmlwriter
  1328.             try:
  1329.                 return htmlwriter.Swing(str(v))
  1330.             except:
  1331.                 return "(?)"
  1332.  
  1333.         li = []
  1334.         theItems = dumpWho.__dict__.keys()
  1335.         theItems.sort(lambda a, b: -(a.upper() < b.upper()))
  1336.         something = 0
  1337.         for k in theItems:
  1338.             v = dumpWho.__dict__[k]
  1339.             if hasattr(dumpWho,"__dependencies__") and (k in dumpWho.__dependencies__):
  1340.                 continue
  1341.             if hasattr(dumpWho,"__configvalues__") and (k in dumpWho.__configvalues__):
  1342.                 continue
  1343.             if hasattr(dumpWho,"__counters__") and (k in dumpWho.__counters__):
  1344.                 continue
  1345.  
  1346.             # service.py variables:
  1347.             if k in ("state","logChannel",'session','boundObjects','__servicelock__','objectConnections','sessionConnections','__sessionfilter__'):
  1348.                 continue # reported elsewhere
  1349.  
  1350.             r = None
  1351.             ok = k
  1352.             if hasattr(dumpWho,"GetHtmlStateDetails"):
  1353.                 r = dumpWho.GetHtmlStateDetails(k,v,k==detail)
  1354.             if r:
  1355.                 k,v = r[0], r[1]
  1356.             else:
  1357.                 if k != detail:
  1358.                     v = Str(v)
  1359.                 else:
  1360.                     import types
  1361.                     var = getattr(dumpWho, detail, None)
  1362.  
  1363.                     if type(var) in [types.ListType, types.TupleType]:
  1364.                         lines = []
  1365.                         for i in var:
  1366.                             lines.append([Str(i)])
  1367.  
  1368.                         v = htmlwriter.OutlineTable([], lines)
  1369.  
  1370.                     elif type(var) == types.DictType:
  1371.                         lines = []
  1372.                         for key, val in var.iteritems():
  1373.                             lines.append([Str(key), Str(val)])
  1374.  
  1375.                         v = htmlwriter.OutlineTable([], lines)
  1376.  
  1377.                     else:
  1378.                         v = Str(v)
  1379.                         v = "<pre>%s</pre>" % (v)
  1380.  
  1381.             k = htmlwriter.Link("", k, [request.query, {"detail": ok}])
  1382.             li.append([k, v])
  1383.             something = 1
  1384.  
  1385.         return htmlwriter.OutlineTable([], li)
  1386.         if not something:
  1387.             return "This service does not have any custom member variables"
  1388.  
  1389.     def HTMLDumpProperties(self, title, page, dumpWho, session, request):
  1390.         import htmlwriter
  1391.         def Str(v):
  1392.             import htmlwriter
  1393.             try:
  1394.                 return htmlwriter.Swing(str(v))
  1395.             except:
  1396.                 return "(?)"
  1397.  
  1398.         hd = ["Property","Value"]
  1399.         li = []
  1400.  
  1401.         deepdependencies = []
  1402.         if hasattr(dumpWho,"GetDeepDependencies"):
  1403.             dumpWho.GetDeepDependencies(deepdependencies)
  1404.  
  1405.         deepdependants = []
  1406.         if hasattr(dumpWho,"GetDeepDependendants"):
  1407.             dumpWho.GetDeepDependants(deepdependants)
  1408.  
  1409.         dependencies = []
  1410.         if hasattr(dumpWho,"__dependencies__"):
  1411.             dependencies = dumpWho.__dependencies__
  1412.  
  1413.         dependants = []
  1414.         if hasattr(dumpWho,"GetDependants"):
  1415.             dependants = dumpWho.GetDependants()
  1416.  
  1417.         sessionparams = []
  1418.         if hasattr(dumpWho,"__sessionparams__"):
  1419.             sessionparams = dumpWho.__sessionparams__
  1420.  
  1421.         persistvars = []
  1422.         if hasattr(dumpWho,"__persistvars__"):
  1423.             persistvars = dumpWho.__persistvars__
  1424.  
  1425.         nonpersistvars = []
  1426.         if hasattr(dumpWho,"__nonpersistvars__"):
  1427.             nonpersistvars = dumpWho.__nonpersistvars__
  1428.  
  1429.         notifyevents = []
  1430.         if hasattr(dumpWho,"__notifyevents__"):
  1431.             notifyevents = dumpWho.__notifyevents__
  1432.  
  1433.         exportedcalls = []
  1434.         if hasattr(dumpWho,"__exportedcalls__"):
  1435.             exportedcalls = dumpWho.__exportedcalls__
  1436.  
  1437.         objectConnections = []
  1438.         if hasattr(dumpWho,"objectConnections"):
  1439.             for each in dumpWho.objectConnections.itervalues():
  1440.                 objectConnections.append( Str(each) )
  1441.  
  1442.         sessionConnections = []
  1443.         if hasattr(dumpWho,"sessionConnections"):
  1444.             for each in dumpWho.sessionConnections.itervalues():
  1445.                 sessionConnections.append( Str(each) )
  1446.  
  1447.         boundObjects = []
  1448.         if hasattr(dumpWho,"boundObjects"):
  1449.             boundObjects = dumpWho.boundObjects.keys()
  1450.  
  1451.         sessionfilter = []
  1452.         if hasattr(dumpWho,"__sessionfilter__"):
  1453.             sessionfilter = dumpWho.__sessionfilter__
  1454.  
  1455.         def Alphabetize(arr):
  1456.             try:
  1457.                 li = arr[:]
  1458.                 li.sort(lambda a, b: -(a.upper() < b.upper()))
  1459.                 return li
  1460.             except:
  1461.                 return arr
  1462.  
  1463.         properties = {
  1464.             "Objects bound to me"               : Alphabetize(boundObjects),
  1465.             "Sessions using me"                 : Alphabetize(sessionConnections),
  1466.             "Objects using me"                  : Alphabetize(objectConnections),
  1467.             "Session Filter"                    : Alphabetize(sessionfilter),
  1468.             "I depend on"                       : Alphabetize(dependencies),
  1469.             "I depend on - recursively"         : Alphabetize(deepdependencies),
  1470.             "Who depends on me"                 : Alphabetize(dependants),
  1471.             "Who depends on me - recursively"   : Alphabetize(deepdependants),
  1472.             "Session Parameters"                : Alphabetize(sessionparams),
  1473.             "Persistant Variables"              : Alphabetize(persistvars),
  1474.             "Non-persistant Variables"          : Alphabetize(nonpersistvars),
  1475.             "I listen to these events"          : Alphabetize(notifyevents),
  1476.             "I export these calls"              : Alphabetize(exportedcalls),
  1477.         }
  1478.  
  1479.         def xyzzy(k,val):
  1480.             if k in ("I depend on","I depend on - recursively","Who depends on me","Who depends on me - recursively"):
  1481.                 return htmlwriter.Link("", str(val), [request.query, {"svcname": str(val)}])
  1482.             elif k=='Objects bound to me':
  1483.                 return htmlwriter.Link("", str(val), [request.query, {"objectDetail": binascii.b2a_hex( cPickle.dumps( val, 1)) }])
  1484.             else:
  1485.                 return str(val)
  1486.  
  1487.         for k in properties:
  1488.             s = ""
  1489.             comma = ""
  1490.             for each in properties[k]:
  1491.                 s += comma + xyzzy(k,each)
  1492.                 comma = ", "
  1493.             if (s != ""):
  1494.                 li.append([k, s])
  1495.  
  1496.         if hasattr(dumpWho,"GetServiceState"):
  1497.             li.append(["Service State", dumpWho.GetServiceState()])
  1498.         if hasattr(dumpWho,"GetServiceState"):
  1499.             li.append(["Service Type",  dumpWho.GetServiceState()])
  1500.         if hasattr(dumpWho,"session"):
  1501.             li.append(["Service Session", str(dumpWho.session)])
  1502.         if hasattr(dumpWho,"__servicelock__"):
  1503.             li.append(["Service Lock", str(getattr(dumpWho,"__servicelock__",None))])
  1504.  
  1505.         li.sort(lambda a, b: -(a[0].upper() < b[0].upper()))
  1506.         return htmlwriter.WebPart(title, htmlwriter.OutlineTable(hd, li), page)
  1507.  
  1508. exports["service.Service"] = Service
  1509.