Beispiel #1
0
class NonRootAPI(object):
    """
    An API that will check for a Non Root API's
    """
    def __init__(self, comp):
        """
        A NonRootAPi constructor will init a logger and a comp property for an object
        that have to be compare with the existing registered protocols

        :param comp:
        :type comp: an input object as DynamicProtocol

        :return: void
        """

        self.comp = comp
        self.__logger = Logger()

    def check(self):
        """
        Will trying to match the candidate Non Root API
        check IJSONResourceAPI for more info

        :return: False if not success otherwise the response from matched API.method()
        """

        for api in subscribers([self.comp], IJSONResourceAPI):

            if api.__class__.__name__.lower() in self.comp.to_dict().keys():

                self.__logger.debug('Candidate API {} for {}'.format(
                    api.__class__.__name__, self.comp.__class__.__name__))

                candidate_api_name = self.comp.to_dict().get(
                    api.__class__.__name__.lower())

                try:
                    # execute the candidate API method
                    # and return the result
                    candidate_api_result = getattr(api, candidate_api_name)()

                except AttributeError as e:

                    msg = 'Candidate API {} for {} does not implement method {} error: {}'

                    self.__logger.warning(
                        msg.format(api.__class__.__name__,
                                   self.comp.__class__.__name__,
                                   candidate_api_name, e.message))

                else:

                    self.__logger.info('Successful apply API {} for {}'.format(
                        api.__class__.__name__, self.comp.__class__.__name__))

                    return candidate_api_result

        return False
Beispiel #2
0
class DefaultWSFactory(object):

    """
    A class that represent a default WS Factory
    """

    def __init__(self, **kwargs):

        """
        Default factory, used for TCP servers, implements IDefaultFactory
        :param kwargs:

        :return:
        """
        self.__logger = Logger()

        self.config = kwargs.get('config', None)

        if self.config is None:

            ws = WS()

            self.__logger.warning('Config for IDefaultFactory is not provided, failback to defaults...')

            self.config = {
                'url': 'ws://localhost:8585',
                'port': 8585,
                'hostname': 'localhost',
                'protocol': 'ws'
            }

            self.config = ws.to_object(self.config)

        self.ws_protocol = self.config.protocol

        self.name = kwargs.get('name', 'DefaultWSFactory')

        self.port = kwargs.get('port', self.config.port)

        self.url = kwargs.get('port', self.config.url)

        self.belong_to = kwargs.get('belong_to', False)

        self.ws_server_factory = DefaultWSFactoryRunner

        if self.ws_protocol == 'wss':

            key = self.config.keys.key
            crt = self.config.keys.crt

            if key is None or crt is None:
                raise AttributeError('WS over SSL required attribute a key and a crt')

            self.crt_keys = dict(key=key, crt=crt)

        self.ws_msg_protocol = DefaultWSProtocol
Beispiel #3
0
class DefaultWSFactory(object):
    """
    A class that represent a default WS Factory
    """
    def __init__(self, **kwargs):
        """
        Default factory, used for TCP servers, implements IDefaultFactory
        :param kwargs:

        :return:
        """
        self.__logger = Logger()

        self.config = kwargs.get('config', None)

        if self.config is None:

            ws = WS()

            self.__logger.warning(
                'Config for IDefaultFactory is not provided, failback to defaults...'
            )

            self.config = {
                'url': 'ws://localhost:8585',
                'port': 8585,
                'hostname': 'localhost',
                'protocol': 'ws'
            }

            self.config = ws.to_object(self.config)

        self.ws_protocol = self.config.protocol

        self.name = kwargs.get('name', 'DefaultWSFactory')

        self.port = kwargs.get('port', self.config.port)

        self.url = kwargs.get('port', self.config.url)

        self.belong_to = kwargs.get('belong_to', False)

        self.ws_server_factory = DefaultWSFactoryRunner

        if self.ws_protocol == 'wss':

            key = self.config.keys.key
            crt = self.config.keys.crt

            if key is None or crt is None:
                raise AttributeError(
                    'WS over SSL required attribute a key and a crt')

            self.crt_keys = dict(key=key, crt=crt)

        self.ws_msg_protocol = DefaultWSProtocol
Beispiel #4
0
class Validator(BaseValidator):

    """
    Our main validator will trying to resolve the type of incoming request
    """

    def __init__(self, validate_msg):
        """
        Accept message that have to be validated

        :param validate_msg: currently we supporting only a json validators
        """
        super(Validator, self).__init__(validate_msg)

        self.__msg = Message(validate_msg)
        self.__logger = Logger()

    def validate(self):

        """
        Will walk through all subscribers from IValidator type
        the first one will be returned as valid type. In this way we can supporting a lot of types
        on the same port/service

        :return: matched IValidator subscriber
        """

        for sub in subscribers([self.__msg], IValidator):

            msg = sub.validate()

            self.is_valid = msg.is_valid
            self.message_type = msg.message_type
            self.message = msg.message

            # we want only one correct type of our message so
            # only one validator will response with True

            if self.is_valid is True:

                self.__logger.debug('Matched type is: {}'.format(self.message_type))

                return self

        self.__logger.warning('Main Validator - There are no subscribers from type IValidator')

        return False
Beispiel #5
0
class Validator(BaseValidator):
    """
    Our main validator will trying to resolve the type of incoming request
    """
    def __init__(self, validate_msg):
        """
        Accept message that have to be validated

        :param validate_msg: currently we supporting only a json validators
        """
        super(Validator, self).__init__(validate_msg)

        self.__msg = Message(validate_msg)
        self.__logger = Logger()

    def validate(self):
        """
        Will walk through all subscribers from IValidator type
        the first one will be returned as valid type. In this way we can supporting a lot of types
        on the same port/service

        :return: matched IValidator subscriber
        """

        for sub in subscribers([self.__msg], IValidator):

            msg = sub.validate()

            self.is_valid = msg.is_valid
            self.message_type = msg.message_type
            self.message = msg.message

            # we want only one correct type of our message so
            # only one validator will response with True

            if self.is_valid is True:

                self.__logger.debug('Matched type is: {}'.format(
                    self.message_type))

                return self

        self.__logger.warning(
            'Main Validator - There are no subscribers from type IValidator')

        return False
Beispiel #6
0
class DefaultFactory(Factory):
    """
    A default TCP Factory
    """
    def __init__(self, **kwargs):

        """
        Default factory, used for TCP servers, implements IDefaultFactory
        :param kwargs:

        """

        self.__logger = Logger()

        self.config = kwargs.get('config', None)

        if self.config is None:

            self.__logger.warning('Config for IDefaultFactory is not provided, failback to defaults...')

            tcp = TCP()

            self.config = {
                'port': 8484,
                'tcp_back_log': 50,
                'service_name': 'Default TCP Server'
            }

            self.config = tcp.to_object(self.config)

        self.protocol = kwargs.get('protocol', DefaultLineReceiver)

        self.name = kwargs.get('name', self.config.service_name)

        self.port = kwargs.get('port', self.config.port)

        self.belong_to = kwargs.get('belong_to', False)
Beispiel #7
0
class DefaultWebFactory(Factory):

    """
    A class which represent our defaults web factory
    """

    def __init__(self, **kwargs):

        """

        Default factory, used for TCP servers, implements IDefaultFactory
        :param kwargs:

        """
        self.__logger = Logger()

        self.config = kwargs.get('config', None)

        if self.config is None:

            web = WEB()

            self.__logger.warning('Config for IDefaultWebFactory is not provided, failback to defaults...')

            self.config = {
                'port': 8000,
                'www_root': '',
                'service_name': 'Default WEB Server',
                'http_methods': ['GET']
            }

            self.config = web.to_object(self.config)

        self.name = kwargs.get('name', self.config.service_name)
        self.port = kwargs.get('port', self.config.port)
        self.methods = kwargs.get('http_methods', self.config.http_methods)
        self.belong_to = kwargs.get('belong_to', False)
Beispiel #8
0
    def subscriber_dispatcher(self, sub_data):
        """
        Callback function for our global subscriber
        will pass sub_data to GlobalSubscribeMessage and then will trying to
        get wamp component which implements IUserGlobalSubscriber and adapts
        IGlobalSubscribeMessage

        :param sub_data:
        """

        log = Logger()

        try:

            result = IDispatcher(Validator(sub_data)).dispatch()

        # NETODO what exception can happen here?
        except Exception as e:

            import traceback
            print traceback.format_exc()

            log.warning('subscriber_dispatcher exception: {}'.format(
                e.message))

        else:

            if IValidator.providedBy(result):
                log.warning('WAMP Message is invalid: {}'.format(
                    result.message))

            if result is not False and IJSONResource.providedBy(result):

                fac = None

                for sub in subscribers([result], IUserGlobalSubscriber):

                    sub.subscribe(self)
                    fac = True

                    break

                if not fac:
                    log.warning(
                        'There are no user definition for IUserGlobalSubscriber, message was skipped'
                    )
Beispiel #9
0
    def subscriber_dispatcher(self, sub_data):

        """
        Callback function for our global subscriber
        will pass sub_data to GlobalSubscribeMessage and then will trying to
        get wamp component which implements IUserGlobalSubscriber and adapts
        IGlobalSubscribeMessage

        :param sub_data:
        """

        log = Logger()

        try:

            result = IDispatcher(Validator(sub_data)).dispatch()

        # NETODO what exception can happen here?
        except Exception as e:

            import traceback
            print traceback.format_exc()

            log.warning('subscriber_dispatcher exception: {}'.format(
                e.message
            ))

        else:

            if IValidator.providedBy(result):
                log.warning('WAMP Message is invalid: {}'.format(result.message))

            if result is not False and IJSONResource.providedBy(result):

                fac = None

                for sub in subscribers([result], IUserGlobalSubscriber):

                    sub.subscribe(self)
                    fac = True

                    break

                if not fac:
                    log.warning('There are no user definition for IUserGlobalSubscriber, message was skipped')
Beispiel #10
0
class AutobahnDefaultFactory(service.Service):
    """
    This class is a convenience tool mainly for development and quick hosting
    of WAMP application components.

    It can host a WAMP application component in a WAMP-over-WebSocket client
    connecting to a WAMP router.
    """

    def __init__(self, **kwargs):
        """

        :param url: The WebSocket URL of the WAMP router to connect to (e.g. `ws://somehost.com:8090/somepath`)
        :type url: unicode
        :param realm: The WAMP realm to join the application session to.
        :type realm: unicode
        :param extra: Optional extra configuration to forward to the application component.
        :type extra: dict
        :param debug: Turn on low-level debugging.
        :type debug: bool
        :param debug_wamp: Turn on WAMP-level debugging.
        :type debug_wamp: bool
        :param debug_app: Turn on app-level debugging.
        :type debug_app: bool

        """

        self.logger = Logger()

        self.config = kwargs.get('config', None)

        if self.config is None:

            self.config = {}
            wamp = WAMP()

            self.logger.warning('Config is not provided failback to defaults')

            self.config.update({
                'protocol': 'wss',
                'hostname': 'localhost',
                'port': 8080, # integer
                'realm': 'realm1',
                'path': 'ws',
                'retry_interval': 2,  # in seconds
                'url': u'ws://localhost:8080/ws',
                'service_name': 'A Default WAMP Service name"'
            })

            self.config = wamp.to_object(self.config)

        self.url = kwargs.get('url', self.config.url)

        self.realm = u'{}'.format(kwargs.get('realm', self.config.realm))

        self.extra = kwargs.get('extra', dict())

        self.debug = kwargs.get('debug', False)

        self.debug_wamp = kwargs.get('debug_wamp', False)

        self.debug_app = kwargs.get('debug_app', False)

        self.belong_to = kwargs.get('belong_to', False)

        self.make = None

        self.protocol = kwargs.get('protocol', self.config.protocol)

        self.name = kwargs.get('name', self.config.service_name)

        self.port = kwargs.get('port', self.config.port)

        self.host = kwargs.get('host', self.config.hostname)

        self.path = kwargs.get('path', self.config.path)

    def run(self, make):
        """
        Run the application component.

        :param make: A factory that produces instances of :class:`autobahn.asyncio.wamp.ApplicationSession`
           when called with an instance of :class:`autobahn.wamp.types.ComponentConfig`.
        :type make: callable
        """

        is_secure, host, port, resource, path, params = parse_url(self.url)

        # start logging to console
        if self.debug or self.debug_wamp or self.debug_app:
            pass
            # log.startLogging(sys.stdout)

        # factory for use ApplicationSession
        def create():
            """
            Will trying to create an ApplicationSession object
            :return: ApplicationSession
            """
            cfg = ComponentConfig(self.realm, self.extra)

            try:
                session = make(config=cfg)

            except Exception as e:

                # the app component could not be created .. fatal
                self.logger.critical('CREATE RUNNER EXCEPTION {}'.format(e.message))

            else:

                session.debug_app = self.debug_app
                return session

        # create a WAMP-over-WebSocket transport client factory
        transport_factory = WampWebSocketClientFactory(
            create,
            url=self.url
        )

        if is_secure:
            endpoint_descriptor = "ssl:{0}:{1}".format(host, port)

        else:
            endpoint_descriptor = "tcp:{0}:{1}".format(host, port)

        try:
            self.logger.info('Trying to connect to: {}'.format(endpoint_descriptor))
            self.connect(endpoint_descriptor, transport_factory, make)

            return self

        except Exception as e:
            self.logger.error('CLIENT CONNECT ERROR: {}'.format(e.message))

    def connect(self, endpoint, transport, session):
        """
        Will make a connection to a WAMP router based on a Twisted endpoint
        :param endpoint:
        :param transport:
        :param session:
        :return:
        """
        try:

            # start the client from a Twisted endpoint

            client = clientFromString(reactor, endpoint)

            res = client.connect(transport)

            def connect_result(result):
                """
                A callback used as defer result
                :param result:
                :return:
                """
                return result

            def connect_error(result):
                """
                A callback used as defer if error occur
                :param result:
                :return:
                """
                self.logger.error('CONNECTION ERROR: {}'.format(result.getErrorMessage()))

                try:

                    reconnect = Reconnect(
                        session=session,
                        runner=AutobahnDefaultFactory,
                        config=self.config
                    )

                    reactor.callLater(
                        self.config.retry_interval,
                        reconnect.start,
                    )

                except Exception as e:
                    self.logger.error('RECONNECTING ERROR: {}'.format(e.message))

            res.addCallback(connect_result)
            res.addErrback(connect_error)

            return res

        except Exception as e:
            self.logger.error('CONNECTION GLOBAL ERRORS: {}'.format(e.message))

    def startService(self):
        """
        Start a WAMP service
        :return:
        """
        service.Service.startService(self)

    def stopService(self):
        """
        Stop a WAMP service
        :return:
        """
        service.Service.stopService(self)
Beispiel #11
0
class WampDefaultComponent(ApplicationSession):
    """
    Class that represent our default WAMP component we inherit from
    autobahn.twisted.wamp.ApplicationSession
    """
    def __init__(self, **kwargs):
        """
        The constructor will init self.cfg self.__gsm and if the protocol is wss,
        will attach on_connect and on_challenge to this object as methods
        :param kwargs: we looking for key 'config'
        :return: void
        """
        config = kwargs.get('config')

        super(WampDefaultComponent, self).__init__(config)

        self.__logger = Logger()
        self.cfg = Config().get_wamp()

        self.__gsm = getGlobalSiteManager()

        if self.cfg.protocol == 'wss':

            self.__logger.info('WAMP is secure, switch to wss...')

            WampDefaultComponent.onConnect = on_connect
            WampDefaultComponent.onChallenge = on_challenge

    def subscriber_dispatcher(self, sub_data):
        """
        Callback function for our global subscriber
        will pass sub_data to GlobalSubscribeMessage and then will trying to
        get wamp component which implements IUserGlobalSubscriber and adapts
        IGlobalSubscribeMessage

        :param sub_data:
        """

        log = Logger()

        try:

            result = IDispatcher(Validator(sub_data)).dispatch()

        # NETODO what exception can happen here?
        except Exception as e:

            import traceback
            print traceback.format_exc()

            log.warning('subscriber_dispatcher exception: {}'.format(
                e.message))

        else:

            if IValidator.providedBy(result):
                log.warning('WAMP Message is invalid: {}'.format(
                    result.message))

            if result is not False and IJSONResource.providedBy(result):

                fac = None

                for sub in subscribers([result], IUserGlobalSubscriber):

                    sub.subscribe(self)
                    fac = True

                    break

                if not fac:
                    log.warning(
                        'There are no user definition for IUserGlobalSubscriber, message was skipped'
                    )

    @inlineCallbacks
    def onJoin(self, details):
        """
        Autobahn callback function that fired when onJoin happens (we join to our realm)

        Here all components recognized as WAMP we will registering on the fly.

        The global subscriber will be started too, its name is formed by
        netcatks_global_subscriber_ + WAMP Service name.

        Also inside our Storage register will be saved the current WAMP session object
        in __wamp_session__ namespace.

        If there an API which providing a IWAMPLoadOnRunTime implementation will be started on
        run time

        :param details:
        :return:
        """
        self.__logger.info('WAMP Session is ready')

        sub_topic = 'netcatks_global_subscriber_{}'.format(
            self.cfg.service_name.lower().replace(' ', '_'))

        yield self.subscribe(self.subscriber_dispatcher, sub_topic)
        self.__logger.info('Starting global subscriber: {}'.format(sub_topic))

        # registration of all classes which ends with Wamp into shared wamp session
        for x in list(self.__gsm.registeredSubscriptionAdapters()):

            # storing wamp session inside storage
            sr = createObject('storageregister').components
            sr['__wamp_session__'] = self

            if IWAMPResource in x.required:

                if x.factory.__name__ != 'BaseWampComponent':

                    f = x.factory()

                    self.__logger.info('Register Wamp Component: {}'.format(
                        f.__class__.__name__))

                    yield self.register(f)

                    # here we provide wamp session to each wamp component,
                    # in this way every component can access methods like publish, call etc,
                    # which are hosted by default inside wamp session.

                    f.set_session(self)

            if x.provided is IWAMPLoadOnRunTime:
                x.factory('init').load()

    def onDisconnect(self):
        """
        Will be fired when we go a disconnecting from a WAMP router
        :return:
        """
        self.__logger.warning('Disconnected...')

        try:

            reconnect = Reconnect(session=WampDefaultComponent,
                                  runner=AutobahnDefaultFactory,
                                  config=self.cfg)

            reactor.callLater(
                self.cfg.retry_interval,
                reconnect.start,
            )

        # NETODO what exception trow here?
        except Exception as e:
            self.__logger.warning('disconnect warn: {}'.format(e.message))
Beispiel #12
0
class DispathcherResultHelper(object):
    """
    A helper class that care about of the result and the response of the request
    """
    def __init__(self, factory):
        """
        Just settings
        :param factory:
        :return: void
        """

        self.__logger = Logger()
        self.factory = factory

    @staticmethod
    def deferred_response(response, sender):
        """
        Care about global defered response
        :param response: response from defer operation
        :param sender: TBA

        :return: void
        """
        if sender is not None:
            sender(response.to_json())

    def deferred_response_error(self, err):
        """
        fired if global defered response fail
        :param err:

        :return: False
        """
        self.__logger.error('Cannot send message to user: {}'.format(err))

        return False

    def result_validation(self, sender=None, drop=None, section='TCP'):
        """
        A Result Helper more info TBA

        :param sender:
        :param drop:
        :param section:

        :return: mixin
        """

        if IValidator.providedBy(self.factory):

            self.__logger.warning('{} Message is invalid: {}'.format(
                section, self.factory.message))

            if drop is not None:
                drop()

            else:
                return 'message is invalid'

        else:

            if self.factory:

                self.__logger.info('{} Response: {}'.format(
                    section, self.factory))

                if IJSONResource.providedBy(self.factory):

                    if sender is not None:
                        sender(self.factory.to_json())

                    else:
                        return self.factory.to_json()

                elif isinstance(self.factory, Deferred):

                    self.factory.addCallback(self.deferred_response, sender)
                    self.factory.addErrback(self.deferred_response_error)

            else:

                self.__logger.warning(
                    '{}: This message was not be dispatched'.format(section))
                drop()
Beispiel #13
0
class NonRootAPI(object):

    """
    An API that will check for a Non Root API's
    """

    def __init__(self, comp):

        """
        A NonRootAPi constructor will init a logger and a comp property for an object
        that have to be compare with the existing registered protocols

        :param comp:
        :type comp: an input object as DynamicProtocol

        :return: void
        """

        self.comp = comp
        self.__logger = Logger()

    def check(self):

        """
        Will trying to match the candidate Non Root API
        check IJSONResourceAPI for more info

        :return: False if not success otherwise the response from matched API.method()
        """

        for api in subscribers([self.comp], IJSONResourceAPI):

            if api.__class__.__name__.lower() in self.comp.to_dict().keys():

                self.__logger.debug('Candidate API {} for {}'.format(
                    api.__class__.__name__,
                    self.comp.__class__.__name__
                ))

                candidate_api_name = self.comp.to_dict().get(api.__class__.__name__.lower())

                try:
                    # execute the candidate API method
                    # and return the result
                    candidate_api_result = getattr(api, candidate_api_name)()

                except AttributeError as e:

                    msg = 'Candidate API {} for {} does not implement method {} error: {}'

                    self.__logger.warning(msg.format(
                        api.__class__.__name__,
                        self.comp.__class__.__name__,
                        candidate_api_name,
                        e.message
                    ))

                else:

                    self.__logger.info('Successful apply API {} for {}'.format(
                        api.__class__.__name__,
                        self.comp.__class__.__name__
                    ))

                    return candidate_api_result

        return False
Beispiel #14
0
class Dispatcher(object):

    """
    Our Main dispatcher, Will trying to dispatch the request to API which provides functionality for it.
    The Dispatcher also adapts IValidator
    """

    adapts(IValidator)

    def __init__(self, validator):

        """
        The constructor will init a logger and will init a public attribute - self.validator
        that is a validated requests, here we have to determinate which protocol and API have
        to process this request

        :param validator:
        :type validator: IValidator

        :return: void
        """

        self.validator = validator
        self.__logger = Logger()

    def __api_processor(self, valid_dispatch, valid_response, isubscriber):

        """

        Will loop through a valid response objects and will compare it to a registered
        protocol subscribers

        :param valid_dispatch:
        :param valid_response:
        :param isubscriber:

        :return: The chosen API or False
        """
        for sub in subscribers([valid_response], isubscriber):

            self.__logger.debug('Matched request subscribers: {}'.format(sub.__class__.__name__))

            try:

                verifyObject(isubscriber, sub)

            except DoesNotImplement as e:

                self.__logger.warning('Incorrect implementation: {}'.format(e))

            else:

                comp = sub.compare()

                if comp is not False and IJSONResource.providedBy(comp):

                    self.__logger.debug('Signature compare to {}'.format(comp.__class__.__name__))

                    # trying to resolve API that will deal with these request

                    if len(comp.to_dict().keys()) > 1:
                        # process request without root element
                        return NonRootAPI(comp).check()

                    else:
                        # root element
                        return RootAPI(comp).check()

        # if there are no one subsciber from IJSONResource

        self.__logger.warning('The request {} from type {} was not recognized as a structure or an API'.format(
            valid_response.response,
            valid_dispatch.message_type
        ))

        return False

    def dispatch(self):

        """
        When request is happening first will be validated,
        if valid_dispatch is False means we do not support this data type. You have to write your custom
        validator(s) inside components/validators

        :return: The response or False
        """

        # validate for supporting types

        try:
            valid_dispatch = self.validator.validate()

        except Exception as e:

            self.__logger.debug('validate error: {}'.format(e.message))
            return self.validator

        else:

            if valid_dispatch is False:
                return self.validator

            valid_response = ValidatorResponse(valid_dispatch.message)

            if valid_dispatch.message_type == 'JSON':

                return self.__api_processor(
                    valid_dispatch,
                    valid_response,
                    IJSONResourceSubscriber
                )
Beispiel #15
0
class AutobahnDefaultFactory(service.Service):
    """
    This class is a convenience tool mainly for development and quick hosting
    of WAMP application components.

    It can host a WAMP application component in a WAMP-over-WebSocket client
    connecting to a WAMP router.
    """
    def __init__(self, **kwargs):
        """

        :param url: The WebSocket URL of the WAMP router to connect to (e.g. `ws://somehost.com:8090/somepath`)
        :type url: unicode
        :param realm: The WAMP realm to join the application session to.
        :type realm: unicode
        :param extra: Optional extra configuration to forward to the application component.
        :type extra: dict
        :param debug: Turn on low-level debugging.
        :type debug: bool
        :param debug_wamp: Turn on WAMP-level debugging.
        :type debug_wamp: bool
        :param debug_app: Turn on app-level debugging.
        :type debug_app: bool

        """

        self.logger = Logger()

        self.config = kwargs.get('config', None)

        if self.config is None:

            self.config = {}
            wamp = WAMP()

            self.logger.warning('Config is not provided failback to defaults')

            self.config.update({
                'protocol': 'wss',
                'hostname': 'localhost',
                'port': 8080,  # integer
                'realm': 'realm1',
                'path': 'ws',
                'retry_interval': 2,  # in seconds
                'url': u'ws://localhost:8080/ws',
                'service_name': 'A Default WAMP Service name"'
            })

            self.config = wamp.to_object(self.config)

        self.url = kwargs.get('url', self.config.url)

        self.realm = u'{}'.format(kwargs.get('realm', self.config.realm))

        self.extra = kwargs.get('extra', dict())

        self.debug = kwargs.get('debug', False)

        self.debug_wamp = kwargs.get('debug_wamp', False)

        self.debug_app = kwargs.get('debug_app', False)

        self.belong_to = kwargs.get('belong_to', False)

        self.make = None

        self.protocol = kwargs.get('protocol', self.config.protocol)

        self.name = kwargs.get('name', self.config.service_name)

        self.port = kwargs.get('port', self.config.port)

        self.host = kwargs.get('host', self.config.hostname)

        self.path = kwargs.get('path', self.config.path)

    def run(self, make):
        """
        Run the application component.

        :param make: A factory that produces instances of :class:`autobahn.asyncio.wamp.ApplicationSession`
           when called with an instance of :class:`autobahn.wamp.types.ComponentConfig`.
        :type make: callable
        """

        is_secure, host, port, resource, path, params = parse_url(self.url)

        # start logging to console
        if self.debug or self.debug_wamp or self.debug_app:
            pass
            # log.startLogging(sys.stdout)

        # factory for use ApplicationSession
        def create():
            """
            Will trying to create an ApplicationSession object
            :return: ApplicationSession
            """
            cfg = ComponentConfig(self.realm, self.extra)

            try:
                session = make(config=cfg)

            except Exception as e:

                # the app component could not be created .. fatal
                self.logger.critical('CREATE RUNNER EXCEPTION {}'.format(
                    e.message))

            else:

                session.debug_app = self.debug_app
                return session

        # create a WAMP-over-WebSocket transport client factory
        transport_factory = WampWebSocketClientFactory(create, url=self.url)

        if is_secure:
            endpoint_descriptor = "ssl:{0}:{1}".format(host, port)

        else:
            endpoint_descriptor = "tcp:{0}:{1}".format(host, port)

        try:
            self.logger.info(
                'Trying to connect to: {}'.format(endpoint_descriptor))
            self.connect(endpoint_descriptor, transport_factory, make)

            return self

        except Exception as e:
            self.logger.error('CLIENT CONNECT ERROR: {}'.format(e.message))

    def connect(self, endpoint, transport, session):
        """
        Will make a connection to a WAMP router based on a Twisted endpoint
        :param endpoint:
        :param transport:
        :param session:
        :return:
        """
        try:

            # start the client from a Twisted endpoint

            client = clientFromString(reactor, endpoint)

            res = client.connect(transport)

            def connect_result(result):
                """
                A callback used as defer result
                :param result:
                :return:
                """
                return result

            def connect_error(result):
                """
                A callback used as defer if error occur
                :param result:
                :return:
                """
                self.logger.error('CONNECTION ERROR: {}'.format(
                    result.getErrorMessage()))

                try:

                    reconnect = Reconnect(session=session,
                                          runner=AutobahnDefaultFactory,
                                          config=self.config)

                    reactor.callLater(
                        self.config.retry_interval,
                        reconnect.start,
                    )

                except Exception as e:
                    self.logger.error('RECONNECTING ERROR: {}'.format(
                        e.message))

            res.addCallback(connect_result)
            res.addErrback(connect_error)

            return res

        except Exception as e:
            self.logger.error('CONNECTION GLOBAL ERRORS: {}'.format(e.message))

    def startService(self):
        """
        Start a WAMP service
        :return:
        """
        service.Service.startService(self)

    def stopService(self):
        """
        Stop a WAMP service
        :return:
        """
        service.Service.stopService(self)
Beispiel #16
0
class DispathcherResultHelper(object):

    """
    A helper class that care about of the result and the response of the request
    """

    def __init__(self, factory):

        """
        Just settings
        :param factory:
        :return: void
        """

        self.__logger = Logger()
        self.factory = factory

    @staticmethod
    def deferred_response(response, sender):

        """
        Care about global defered response
        :param response: response from defer operation
        :param sender: TBA

        :return: void
        """
        if sender is not None:
            sender(response.to_json())

    def deferred_response_error(self, err):

        """
        fired if global defered response fail
        :param err:

        :return: False
        """
        self.__logger.error('Cannot send message to user: {}'.format(
            err
        ))

        return False

    def result_validation(self, sender=None, drop=None, section='TCP'):

        """
        A Result Helper more info TBA

        :param sender:
        :param drop:
        :param section:

        :return: mixin
        """

        if IValidator.providedBy(self.factory):

            self.__logger.warning('{} Message is invalid: {}'.format(section, self.factory.message))

            if drop is not None:
                drop()

            else:
                return 'message is invalid'

        else:

            if self.factory:

                self.__logger.info('{} Response: {}'.format(section, self.factory))

                if IJSONResource.providedBy(self.factory):

                    if sender is not None:
                        sender(self.factory.to_json())

                    else:
                        return self.factory.to_json()

                elif isinstance(self.factory, Deferred):

                    self.factory.addCallback(self.deferred_response, sender)
                    self.factory.addErrback(self.deferred_response_error)

            else:

                self.__logger.warning('{}: This message was not be dispatched'.format(section))
                drop()
Beispiel #17
0
class WampDefaultComponent(ApplicationSession):

    """
    Class that represent our default WAMP component we inherit from
    autobahn.twisted.wamp.ApplicationSession
    """

    def __init__(self, **kwargs):
        """
        The constructor will init self.cfg self.__gsm and if the protocol is wss,
        will attach on_connect and on_challenge to this object as methods
        :param kwargs: we looking for key 'config'
        :return: void
        """
        config = kwargs.get('config')

        super(WampDefaultComponent, self).__init__(config)

        self.__logger = Logger()
        self.cfg = Config().get_wamp()

        self.__gsm = getGlobalSiteManager()

        if self.cfg.protocol == 'wss':

            self.__logger.info('WAMP is secure, switch to wss...')

            WampDefaultComponent.onConnect = on_connect
            WampDefaultComponent.onChallenge = on_challenge

    def subscriber_dispatcher(self, sub_data):

        """
        Callback function for our global subscriber
        will pass sub_data to GlobalSubscribeMessage and then will trying to
        get wamp component which implements IUserGlobalSubscriber and adapts
        IGlobalSubscribeMessage

        :param sub_data:
        """

        log = Logger()

        try:

            result = IDispatcher(Validator(sub_data)).dispatch()

        # NETODO what exception can happen here?
        except Exception as e:

            import traceback
            print traceback.format_exc()

            log.warning('subscriber_dispatcher exception: {}'.format(
                e.message
            ))

        else:

            if IValidator.providedBy(result):
                log.warning('WAMP Message is invalid: {}'.format(result.message))

            if result is not False and IJSONResource.providedBy(result):

                fac = None

                for sub in subscribers([result], IUserGlobalSubscriber):

                    sub.subscribe(self)
                    fac = True

                    break

                if not fac:
                    log.warning('There are no user definition for IUserGlobalSubscriber, message was skipped')



    @inlineCallbacks
    def onJoin(self, details):
        """
        Autobahn callback function that fired when onJoin happens (we join to our realm)

        Here all components recognized as WAMP we will registering on the fly.

        The global subscriber will be started too, its name is formed by
        netcatks_global_subscriber_ + WAMP Service name.

        Also inside our Storage register will be saved the current WAMP session object
        in __wamp_session__ namespace.

        If there an API which providing a IWAMPLoadOnRunTime implementation will be started on
        run time

        :param details:
        :return:
        """
        self.__logger.info('WAMP Session is ready')

        sub_topic = 'netcatks_global_subscriber_{}'.format(
            self.cfg.service_name.lower().replace(' ', '_')
        )

        yield self.subscribe(self.subscriber_dispatcher, sub_topic)
        self.__logger.info('Starting global subscriber: {}'.format(sub_topic))

        # registration of all classes which ends with Wamp into shared wamp session
        for x in list(self.__gsm.registeredSubscriptionAdapters()):

            # storing wamp session inside storage
            sr = createObject('storageregister').components
            sr['__wamp_session__'] = self

            if IWAMPResource in x.required:

                if x.factory.__name__ != 'BaseWampComponent':

                    f = x.factory()

                    self.__logger.info('Register Wamp Component: {}'.format(f.__class__.__name__))

                    yield self.register(f)

                    # here we provide wamp session to each wamp component,
                    # in this way every component can access methods like publish, call etc,
                    # which are hosted by default inside wamp session.

                    f.set_session(self)

            if x.provided is IWAMPLoadOnRunTime:
                x.factory('init').load()

    def onDisconnect(self):

        """
        Will be fired when we go a disconnecting from a WAMP router
        :return:
        """
        self.__logger.warning('Disconnected...')

        try:

            reconnect = Reconnect(
                session=WampDefaultComponent,
                runner=AutobahnDefaultFactory,
                config=self.cfg
            )

            reactor.callLater(
                self.cfg.retry_interval,
                reconnect.start,
            )

        # NETODO what exception trow here?
        except Exception as e:
            self.__logger.warning('disconnect warn: {}'.format(e.message))
Beispiel #18
0
class Dispatcher(object):
    """
    Our Main dispatcher, Will trying to dispatch the request to API which provides functionality for it.
    The Dispatcher also adapts IValidator
    """

    adapts(IValidator)

    def __init__(self, validator):
        """
        The constructor will init a logger and will init a public attribute - self.validator
        that is a validated requests, here we have to determinate which protocol and API have
        to process this request

        :param validator:
        :type validator: IValidator

        :return: void
        """

        self.validator = validator
        self.__logger = Logger()

    def __api_processor(self, valid_dispatch, valid_response, isubscriber):
        """

        Will loop through a valid response objects and will compare it to a registered
        protocol subscribers

        :param valid_dispatch:
        :param valid_response:
        :param isubscriber:

        :return: The chosen API or False
        """
        for sub in subscribers([valid_response], isubscriber):

            self.__logger.debug('Matched request subscribers: {}'.format(
                sub.__class__.__name__))

            try:

                verifyObject(isubscriber, sub)

            except DoesNotImplement as e:

                self.__logger.warning('Incorrect implementation: {}'.format(e))

            else:

                comp = sub.compare()

                if comp is not False and IJSONResource.providedBy(comp):

                    self.__logger.debug('Signature compare to {}'.format(
                        comp.__class__.__name__))

                    # trying to resolve API that will deal with these request

                    if len(comp.to_dict().keys()) > 1:
                        # process request without root element
                        return NonRootAPI(comp).check()

                    else:
                        # root element
                        return RootAPI(comp).check()

        # if there are no one subsciber from IJSONResource

        self.__logger.warning(
            'The request {} from type {} was not recognized as a structure or an API'
            .format(valid_response.response, valid_dispatch.message_type))

        return False

    def dispatch(self):
        """
        When request is happening first will be validated,
        if valid_dispatch is False means we do not support this data type. You have to write your custom
        validator(s) inside components/validators

        :return: The response or False
        """

        # validate for supporting types

        try:
            valid_dispatch = self.validator.validate()

        except Exception as e:

            self.__logger.debug('validate error: {}'.format(e.message))
            return self.validator

        else:

            if valid_dispatch is False:
                return self.validator

            valid_response = ValidatorResponse(valid_dispatch.message)

            if valid_dispatch.message_type == 'JSON':

                return self.__api_processor(valid_dispatch, valid_response,
                                            IJSONResourceSubscriber)