From d270b7f11e404a3904fa32ed581196a4fa4e8604 Mon Sep 17 00:00:00 2001
From: "Kali Kaneko (leap communications)" <kali@leap.se>
Date: Wed, 5 Oct 2016 15:25:01 -0400
Subject: [refactor] migrate hooks to bitmask repo

now we can deprecate service_hooks in leap.common repo
---
 src/leap/bitmask/bonafide/service.py   |  2 +-
 src/leap/bitmask/core/dummy.py         |  2 +-
 src/leap/bitmask/core/mail_services.py |  3 +-
 src/leap/bitmask/hooks.py              | 77 ++++++++++++++++++++++++++++++++++
 4 files changed, 80 insertions(+), 4 deletions(-)
 create mode 100644 src/leap/bitmask/hooks.py

(limited to 'src')

diff --git a/src/leap/bitmask/bonafide/service.py b/src/leap/bitmask/bonafide/service.py
index 0cd1053..d48a54a 100644
--- a/src/leap/bitmask/bonafide/service.py
+++ b/src/leap/bitmask/bonafide/service.py
@@ -22,8 +22,8 @@ import os
 from collections import defaultdict
 
 from leap.common.config import get_path_prefix
-from leap.common.service_hooks import HookableService
 from leap.bitmask.bonafide._protocol import BonafideProtocol
+from leap.bitmask.hooks import HookableService
 
 from twisted.internet import defer
 from twisted.logger import Logger
diff --git a/src/leap/bitmask/core/dummy.py b/src/leap/bitmask/core/dummy.py
index 64ae2b6..455756c 100644
--- a/src/leap/bitmask/core/dummy.py
+++ b/src/leap/bitmask/core/dummy.py
@@ -19,7 +19,7 @@ An authoritative dummy backend for tests.
 """
 import json
 
-from leap.common.service_hooks import HookableService
+from leap.bitmask.hooks import HookableService
 
 
 class BackendCommands(object):
diff --git a/src/leap/bitmask/core/mail_services.py b/src/leap/bitmask/core/mail_services.py
index bd7f557..564f432 100644
--- a/src/leap/bitmask/core/mail_services.py
+++ b/src/leap/bitmask/core/mail_services.py
@@ -33,9 +33,8 @@ from twisted.internet import reactor
 from twisted.internet import task
 from twisted.logger import Logger
 
-# TODO move to bitmask.common
-from leap.common.service_hooks import HookableService
 from leap.common.files import check_and_fix_urw_only
+from leap.bitmask.hooks import HookableService
 from leap.bitmask.bonafide import config
 from leap.bitmask.keymanager import KeyManager
 from leap.bitmask.keymanager.errors import KeyNotFound
diff --git a/src/leap/bitmask/hooks.py b/src/leap/bitmask/hooks.py
new file mode 100644
index 0000000..7d9825e
--- /dev/null
+++ b/src/leap/bitmask/hooks.py
@@ -0,0 +1,77 @@
+# -*- coding: utf-8 -*-
+# hooks.py
+# Copyright (C) 2016 LEAP
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+"""
+Hooks for service composition.
+"""
+from collections import defaultdict
+
+from twisted.application.service import IService, Service
+from twisted.python import log
+
+from zope.interface import implementer
+
+
+@implementer(IService)
+class HookableService(Service):
+
+    """
+    This service allows for other services in a Twisted Service tree to be
+    notified whenever a certain kind of hook is triggered.
+
+    During the service composition, one is expected to register
+    a hook name with the name of the service that wants to react to the
+    triggering of the hook. All the services, both hooked and listeners, should
+    be registered against the same parent service.
+
+    Upon the hook being triggered, the method "hook_<name>" will be called with
+    the passed data in the listener service.
+    """
+
+    def register_hook(self, name, listener):
+        if not hasattr(self, 'event_listeners'):
+            self.event_listeners = defaultdict(list)
+        log.msg("Registering hook %s->%s" % (name, listener))
+        self.event_listeners[name].append(listener)
+
+    def trigger_hook(self, name, **data):
+
+        def react_to_hook(listener, name, **kw):
+            try:
+                getattr(listener, 'hook_' + name)(**kw)
+            except AttributeError as exc:
+                raise RuntimeError(
+                    "Tried to notify a hook, but the listener service class %s "
+                    "has not defined the proper method %s" %
+                    (listener.__class__, 'hook_' + name))
+
+        if not hasattr(self, 'event_listeners'):
+            self.event_listeners = defaultdict(list)
+        listeners = self._get_listener_services(name)
+        if listeners:
+            for listener in listeners:
+                react_to_hook(listener, name, **data)
+
+    def _get_sibling_service(self, name):
+        if self.parent:
+            return self.parent.getServiceNamed(name)
+
+    def _get_listener_services(self, hook):
+        if hook in self.event_listeners:
+            service_names = self.event_listeners[hook]
+            services = [
+                self._get_sibling_service(name) for name in service_names]
+            return services
-- 
cgit v1.2.3