<commit_before>#!/usr/bin/env python
# -- Content-Encoding: UTF-8 --
"""
Pelix remote services: Common dispatcher

Calls services according to the given method name and parameters

:author: <NAME>
:copyright: Copyright 2015, isandlaTech
:license: Apache License 2.0
:version: 0.6.0
:status: Beta

..

    Copyright 2015 isandlaTech

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
"""

# Module version
__version_info__ = (0, 6, 0)
__version__ = ".".join(str(x) for x in __version_info__)

# Documentation strings format
__docformat__ = "restructuredtext en"

# ------------------------------------------------------------------------------

# iPOPO decorators
from pelix.ipopo.decorators import ComponentFactory, Requires, Provides, \
    BindField, Property, Validate, Invalidate, Instantiate, UnbindField
from pelix.utilities import to_str

# Pelix constants
import pelix.constants
import pelix.framework
import pelix.http
import pelix.remote
import pelix.remote.beans as beans

# Standard library
import json
import logging
import threading

try:
    # Python 3
    # pylint: disable=F0401,E0611
    from urllib.parse import urljoin
    import http.client as httplib
except ImportError:
    # Python 2 or IronPython
    # pylint: disable=F0401
    from urlparse import urljoin
    import httplib

# ------------------------------------------------------------------------------

_logger = logging.getLogger(__name__)

# ------------------------------------------------------------------------------


@ComponentFactory('pelix-remote-dispatcher-factory')
@Provides(pelix.remote.SERVICE_DISPATCHER)
@Requires('_exporters', pelix.remote.SERVICE_EXPORT_PROVIDER, True, True)
@Requires('_listeners', pelix.remote.SERVICE_EXPORT_ENDPOINT_LISTENER,
          aggregate=True, optional=True)
@Instantiate('pelix-remote-dispatcher')
class Dispatcher(object):
    """
    Common dispatcher for all exporters
    """
    def __init__(self):
        """
        Sets up the component
        """
        # Remote Service providers
        self._exporters = []

        # Injected listeners
        self._listeners = []

        # Bundle context
        self._context = None

        # Framework UID
        self._fw_uid = None

        # Kind -> {Name -> Endpoint}
        self.__kind_endpoints = {}

        # UID -> Endpoint
        self.__endpoints = {}
        self.__endpoints_lock = threading.Lock()

        # UID -> Exporter
        self.__uid_exporter = {}

        # Service Reference -> set(UID)
        self.__service_uids = {}
        self.__exporters_lock = threading.Lock()

    @Validate
    def _validate(self, context):
        """
        Component validated
        """
        # Get the framework UID
        self._fw_uid = context.get_property(pelix.constants.FRAMEWORK_UID)
        self._context = context

        # Prepare the export LDAP filter
        ldapfilter = '(|({0}=*)({1}=*))' \
            .format(pelix.remote.PROP_EXPORTED_CONFIGS,
                    pelix.remote.PROP_EXPORTED_INTERFACES)

        # Export existing services
        existing_ref = context.get_all_service_references(None, ldapfilter)
        if existing_ref is not None:
            for reference in existing_ref:
                self.__export_service(reference)

        # Register a service listener, to update the exported services state
        context.add_service_listener(self, ldapfilter)

    @Invalidate
    def _invalidate(self, context):
        """
        Component invalidated: clean up storage
        """
        # Unregister the service listener
        context.remove_service_listener(self)
        self._context = None
        self._fw_uid = None

    @staticmethod
    def _compute_endpoint_name(properties):
        """
        Computes the end point name according to service properties

        :param properties: Service properties
        :return: The computed end point name
        """
        name = properties.get(pelix.remote.PROP_ENDPOINT_NAME)
        if not name:
            name = "service_{0}".format(properties[pelix.constants.SERVICE_ID])

        return name

    def _check_name_reuse(self, name):
        """
        Checks if a service was waiting to reuse an endpoint name

        :param name: The endpoint name to reuse
        """
        ldap_filter = "({0}={1})".format(pelix.remote.PROP_ENDPOINT_NAME, name)
        svc_ref = self._context.get_service_reference(None, ldap_filter)
        if svc_ref is not None:
            # A service wants to be exported with the given endpoint name
            _logger.debug("Reuse endpoint name %s with %s", name, svc_ref)
            self.__export_service(svc_ref)

    def service_changed(self, event):
        """
        Called when a service event is triggered
        """
        kind = event.get_kind()
        svc_ref = event.get_service_reference()

        with self.__exporters_lock:
            if kind == pelix.framework.ServiceEvent.REGISTERED:
                # Simply export the service
                self.__export_service(svc_ref)

            elif kind == pelix.framework.ServiceEvent.MODIFIED:
                # Matching registering or updated service
                if svc_ref not in self.__service_uids:
                    # New match
                    self.__export_service(svc_ref)

                else:
                    # Properties modification:
                    # Re-export if endpoint.name has changed
                    self.__update_service(svc_ref,
                                          event.get_previous_properties())
<commit_msg>Avoid exporting imported services when handling endpoint name reuse<commit_after>#!/usr/bin/env python
# -- Content-Encoding: UTF-8 --
"""
Pelix remote services: Common dispatcher

Calls services according to the given method name and parameters

:author: <NAME>
:copyright: Copyright 2015, isandlaTech
:license: Apache License 2.0
:version: 0.6.1
:status: Beta

..

    Copyright 2015 isandlaTech

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
"""

# Module version
__version_info__ = (0, 6, 0)
__version__ = ".".join(str(x) for x in __version_info__)

# Documentation strings format
__docformat__ = "restructuredtext en"

# ------------------------------------------------------------------------------

# iPOPO decorators
from pelix.ipopo.decorators import ComponentFactory, Requires, Provides, \
    BindField, Property, Validate, Invalidate, Instantiate, UnbindField
from pelix.utilities import to_str

# Pelix constants
import pelix.constants
import pelix.framework
import pelix.http
import pelix.remote
import pelix.remote.beans as beans

# Standard library
import json
import logging
import threading

try:
    # Python 3
    # pylint: disable=F0401,E0611
    from urllib.parse import urljoin
    import http.client as httplib
except ImportError:
    # Python 2 or IronPython
    # pylint: disable=F0401
    from urlparse import urljoin
    import httplib

# ------------------------------------------------------------------------------

_logger = logging.getLogger(__name__)

# ------------------------------------------------------------------------------


@ComponentFactory('pelix-remote-dispatcher-factory')
@Provides(pelix.remote.SERVICE_DISPATCHER)
@Requires('_exporters', pelix.remote.SERVICE_EXPORT_PROVIDER, True, True)
@Requires('_listeners', pelix.remote.SERVICE_EXPORT_ENDPOINT_LISTENER,
          aggregate=True, optional=True)
@Instantiate('pelix-remote-dispatcher')
class Dispatcher(object):
    """
    Common dispatcher for all exporters
    """
    def __init__(self):
        """
        Sets up the component
        """
        # Remote Service providers
        self._exporters = []

        # Injected listeners
        self._listeners = []

        # Bundle context
        self._context = None

        # Framework UID
        self._fw_uid = None

        # Kind -> {Name -> Endpoint}
        self.__kind_endpoints = {}

        # UID -> Endpoint
        self.__endpoints = {}
        self.__endpoints_lock = threading.Lock()

        # UID -> Exporter
        self.__uid_exporter = {}

        # Service Reference -> set(UID)
        self.__service_uids = {}
        self.__exporters_lock = threading.Lock()

    @Validate
    def _validate(self, context):
        """
        Component validated
        """
        # Get the framework UID
        self._fw_uid = context.get_property(pelix.constants.FRAMEWORK_UID)
        self._context = context

        # Prepare the export LDAP filter
        ldapfilter = '(|({0}=*)({1}=*))' \
            .format(pelix.remote.PROP_EXPORTED_CONFIGS,
                    pelix.remote.PROP_EXPORTED_INTERFACES)

        # Export existing services
        existing_ref = context.get_all_service_references(None, ldapfilter)
        if existing_ref is not None:
            for reference in existing_ref:
                self.__export_service(reference)

        # Register a service listener, to update the exported services state
        context.add_service_listener(self, ldapfilter)

    @Invalidate
    def _invalidate(self, context):
        """
        Component invalidated: clean up storage
        """
        # Unregister the service listener
        context.remove_service_listener(self)
        self._context = None
        self._fw_uid = None

    @staticmethod
    def _compute_endpoint_name(properties):
        """
        Computes the end point name according to service properties

        :param properties: Service properties
        :return: The computed end point name
        """
        name = properties.get(pelix.remote.PROP_ENDPOINT_NAME)
        if not name:
            name = "service_{0}".format(properties[pelix.constants.SERVICE_ID])

        return name

    def _check_name_reuse(self, name):
        """
        Checks if a service was waiting to reuse an endpoint name

        :param name: The endpoint name to reuse
        """
        ldap_filter = "(&({0}={1})(!({2}=*)))".format(
            pelix.remote.PROP_ENDPOINT_NAME, name, pelix.remote.PROP_IMPORTED)
        svc_ref = self._context.get_service_reference(None, ldap_filter)
        if svc_ref is not None:
            # A service wants to be exported with the given endpoint name
            _logger.debug("Reuse endpoint name %s with %s", name, svc_ref)
            self.__export_service(svc_ref)

    def service_changed(self, event):
        """
        Called when a service event is triggered
        """
        kind = event.get_kind()
        svc_ref = event.get_service_reference()

        with self.__exporters_lock:
            if kind == pelix.framework.ServiceEvent.REGISTERED:
                # Simply export the service
                self.__export_service(svc_ref)

            elif kind == pelix.framework.ServiceEvent.MODIFIED:
                # Matching registering or updated service
                if svc_ref not in self.__service_uids:
                    # New match
                    self.__export_service(svc_ref)

                else:
                    # Properties modification:
                    # Re-export if endpoint.name has changed
                    self.__update_service(svc_ref,
                                          event.get_previous_properties())

{'fraction_non_alphanumeric': 0.11570450097847358, 'fraction_numerical': 0.0066046966731898235, 'mean_word_length': 2.153767035227565, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3365376', 'n_tokens_mistral': 3362, 'n_tokens_neox': 3206, 'n_words': 930}