2017-12-17 16:15:44 +01:00
|
|
|
import importlib
|
2017-11-09 05:04:48 +01:00
|
|
|
import logging
|
2017-12-13 04:14:46 +01:00
|
|
|
import sys
|
2017-11-09 05:04:48 +01:00
|
|
|
|
|
|
|
from threading import Thread
|
|
|
|
|
2017-12-18 03:09:38 +01:00
|
|
|
from platypush.bus import Bus
|
2017-12-18 01:10:51 +01:00
|
|
|
from platypush.config import Config
|
2017-12-17 16:15:44 +01:00
|
|
|
from platypush.message import Message
|
|
|
|
from platypush.message.request import Request
|
|
|
|
from platypush.message.response import Response
|
2017-11-09 05:04:48 +01:00
|
|
|
|
|
|
|
class Backend(Thread):
|
2017-12-17 16:15:44 +01:00
|
|
|
""" Parent class for backends """
|
|
|
|
|
|
|
|
def __init__(self, bus=None, **kwargs):
|
|
|
|
"""
|
|
|
|
Params:
|
|
|
|
bus -- Reference to the Platypush bus where the requests and the
|
2017-12-18 03:09:38 +01:00
|
|
|
responses will be posted [Bus]
|
2017-12-17 16:15:44 +01:00
|
|
|
kwargs -- key-value configuration for this backend [Dict]
|
|
|
|
"""
|
|
|
|
|
|
|
|
# If no bus is specified, create an internal queue where
|
|
|
|
# the received messages will be pushed
|
2017-12-18 03:09:38 +01:00
|
|
|
self.bus = bus if bus else Bus()
|
2017-12-18 01:10:51 +01:00
|
|
|
self.device_id = Config.get('device_id')
|
2017-12-17 16:15:44 +01:00
|
|
|
self.msgtypes = {}
|
2017-11-09 05:04:48 +01:00
|
|
|
|
|
|
|
Thread.__init__(self)
|
2017-12-18 01:10:51 +01:00
|
|
|
logging.basicConfig(stream=sys.stdout, level=Config.get('logging')
|
2017-12-17 16:15:44 +01:00
|
|
|
if 'logging' not in kwargs
|
|
|
|
else getattr(logging, kwargs['logging']))
|
2017-11-09 05:04:48 +01:00
|
|
|
|
2017-12-11 16:48:28 +01:00
|
|
|
def is_local(self):
|
2017-12-17 16:15:44 +01:00
|
|
|
""" Returns true if this is a local backend """
|
2017-12-11 20:30:57 +01:00
|
|
|
from platypush.backend.local import LocalBackend
|
2017-12-11 16:48:28 +01:00
|
|
|
return isinstance(self, LocalBackend)
|
|
|
|
|
2017-12-17 16:15:44 +01:00
|
|
|
def _get_msgtype_class(self, msgtype):
|
|
|
|
""" Gets the class of a message type """
|
|
|
|
|
|
|
|
if msgtype in self.msgtypes: return self.msgtypes[msgtype]
|
|
|
|
|
|
|
|
try:
|
|
|
|
module = importlib.import_module('platypush.message.' + msgtype)
|
|
|
|
except ModuleNotFoundError as e:
|
|
|
|
logging.warn('Unsupported message type {}'.format(msgtype))
|
|
|
|
raise RuntimeError(e)
|
|
|
|
|
|
|
|
cls_name = msgtype[0].upper() + msgtype[1:]
|
|
|
|
|
|
|
|
try:
|
|
|
|
msgclass = getattr(module, cls_name)
|
|
|
|
self.msgtypes[msgtype] = msgclass
|
|
|
|
except AttributeError as e:
|
|
|
|
logging.warn('No such class in {}: {}'.format(
|
|
|
|
module.__name__, cls_name))
|
|
|
|
raise RuntimeError(e)
|
|
|
|
|
|
|
|
return msgclass
|
|
|
|
|
|
|
|
|
2017-11-09 05:04:48 +01:00
|
|
|
def on_msg(self, msg):
|
2017-12-17 16:15:44 +01:00
|
|
|
"""
|
|
|
|
Callback when a message is received on the backend.
|
|
|
|
It parses and posts the message on the main bus.
|
|
|
|
It should be called by the derived classes whenever
|
|
|
|
a new message should be processed.
|
|
|
|
|
|
|
|
Params:
|
|
|
|
msg -- The message. It can be either a key-value
|
|
|
|
dictionary, a platypush.message.Message
|
|
|
|
object, or a string/byte UTF-8 encoded string
|
|
|
|
"""
|
|
|
|
|
|
|
|
msg = Message.parse(msg)
|
|
|
|
if 'type' not in msg:
|
|
|
|
logging.warn('Ignoring message with no type: {}'.format(msg))
|
|
|
|
return
|
|
|
|
|
|
|
|
msgtype = self._get_msgtype_class(msg['type'])
|
|
|
|
msg = msgtype.build(msg)
|
2017-12-13 23:55:38 +01:00
|
|
|
|
2017-12-17 16:15:44 +01:00
|
|
|
if not getattr(msg, 'target') or (msg.target != self.device_id and not self.is_local()):
|
2017-11-09 05:04:48 +01:00
|
|
|
return # Not for me
|
|
|
|
|
2017-12-17 16:15:44 +01:00
|
|
|
logging.debug('Message received on the backend: {}'.format(msg))
|
|
|
|
msg.backend = self # Augment message
|
2017-12-18 01:10:51 +01:00
|
|
|
self.bus.post(msg)
|
2017-12-16 04:56:43 +01:00
|
|
|
|
2017-12-17 16:15:44 +01:00
|
|
|
def send_request(self, request):
|
|
|
|
"""
|
|
|
|
Send a request message on the backend
|
|
|
|
Params:
|
|
|
|
request -- The request, either a dict, a string/bytes UTF-8 JSON,
|
|
|
|
or a platypush.message.request.Request object
|
|
|
|
"""
|
|
|
|
|
|
|
|
request = Request.build(request)
|
|
|
|
assert isinstance(request, Request)
|
|
|
|
|
|
|
|
request.origin = self.device_id
|
|
|
|
self._send_msg(request)
|
|
|
|
|
|
|
|
def send_response(self, response):
|
|
|
|
"""
|
|
|
|
Send a response message on the backend
|
|
|
|
Params:
|
|
|
|
response -- The response, either a dict, a string/bytes UTF-8 JSON,
|
|
|
|
or a platypush.message.response.Response object
|
|
|
|
"""
|
|
|
|
|
|
|
|
response = Response.build(response)
|
|
|
|
assert isinstance(response, Response)
|
2017-11-09 05:04:48 +01:00
|
|
|
|
2017-12-17 16:15:44 +01:00
|
|
|
response.origin = self.device_id
|
|
|
|
self._send_msg(response)
|
2017-11-09 05:04:48 +01:00
|
|
|
|
2017-12-17 16:15:44 +01:00
|
|
|
def _send_msg(self, msg):
|
|
|
|
"""
|
|
|
|
Sends a platypush.message.Message to a node.
|
|
|
|
To be implemented in the derived classes.
|
|
|
|
Always call send_request or send_response instead of _send_msg directly
|
2017-12-16 04:56:43 +01:00
|
|
|
|
2017-12-17 16:15:44 +01:00
|
|
|
Param:
|
|
|
|
msg -- The message
|
|
|
|
"""
|
2017-12-16 04:56:43 +01:00
|
|
|
|
2017-12-17 16:15:44 +01:00
|
|
|
raise NotImplementedError("_send_msg should be implemented in a derived class")
|
2017-11-29 02:42:36 +01:00
|
|
|
|
2017-11-09 05:04:48 +01:00
|
|
|
def run(self):
|
2017-12-17 16:15:44 +01:00
|
|
|
""" Starts the backend thread. To be implemented in the derived classes """
|
|
|
|
raise NotImplementedError("run should be implemented in a derived class")
|
2017-11-09 05:04:48 +01:00
|
|
|
|
2017-12-18 22:40:56 +01:00
|
|
|
def stop(self):
|
|
|
|
""" Stops the backend thread (default: do nothing) """
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2017-11-09 05:04:48 +01:00
|
|
|
# vim:sw=4:ts=4:et:
|
|
|
|
|