Package eoxserver :: Package core :: Module registry
[hide private]
[frames] | no frames]

Source Code for Module eoxserver.core.registry

   1  #------------------------------------------------------------------------------- 
   2  # $Id: registry.py 2123 2012-11-20 15:22:17Z schindlerf $ 
   3  # 
   4  # Project: EOxServer <http://eoxserver.org> 
   5  # Authors: Stephan Krause <stephan.krause@eox.at> 
   6  #          Stephan Meissl <stephan.meissl@eox.at> 
   7  # 
   8  #------------------------------------------------------------------------------- 
   9  # Copyright (C) 2011 EOX IT Services GmbH 
  10  # 
  11  # Permission is hereby granted, free of charge, to any person obtaining a copy 
  12  # of this software and associated documentation files (the "Software"), to deal 
  13  # in the Software without restriction, including without limitation the rights 
  14  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell  
  15  # copies of the Software, and to permit persons to whom the Software is  
  16  # furnished to do so, subject to the following conditions: 
  17  # 
  18  # The above copyright notice and this permission notice shall be included in all 
  19  # copies of this Software or works derived from this Software. 
  20  # 
  21  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
  22  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
  23  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
  24  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
  25  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
  26  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
  27  # THE SOFTWARE. 
  28  #------------------------------------------------------------------------------- 
  29   
  30  """ 
  31  This module contains the implementation of the registry as well as associated 
  32  interface declarations. The registry is the core component of EOxServer that 
  33  links different parts of the system together. The registry allows for 
  34  components to bind to implementations of registered interfaces. It supports 
  35  modularity, extensibility and flexibility of EOxServer. 
  36  """ 
  37   
  38  import imp 
  39  import os.path 
  40  from inspect import isclass 
  41   
  42  from django.conf import settings 
  43   
  44  from eoxserver.core.models import Component 
  45  from eoxserver.core.exceptions import (InternalError, ConfigError, 
  46      ImplementationNotFound, ImplementationAmbiguous, 
  47      ImplementationDisabled, BindingMethodError 
  48  ) 
  49  from eoxserver.core.interfaces import * 
  50  from eoxserver.core.util.filetools import findFiles, pathToModuleName 
51 52 -class Registry(object):
53 """ 54 The :class:`Registry` class implements the functionalities for detecting, 55 registering, finding and binding to implementations of registered 56 interfaces. It is instantiated by :class:`eoxserver.core.system.System` 57 during the startup process. 58 59 The constructor expects a :class:`~.Config` instance as input. The values 60 will be validate and read using a :class:`RegistryConfigReader` instance. 61 """ 62
63 - def __init__(self, config):
64 self.config = config 65 66 self.__intf_index = {} 67 self.__impl_index = {} 68 self.__kvp_index = {} 69 self.__fact_index = {}
70
71 - def bind(self, impl_id):
72 """ 73 Bind to the implementation with ID ``impl_id``. This method returns 74 a new instance of the requested implementation if it is enabled. 75 76 If the implementation is disabled :exc:`~.ImplementationDisabled` will 77 be raised. If the ID ``impl_id`` is not known to the registry 78 :exc:`ImplementationNotFound` will be raised. 79 """ 80 81 if impl_id in self.__impl_index: 82 if self.__impl_index[impl_id]["enabled"]: 83 return self.__impl_index[impl_id]["cls"]() 84 else: 85 raise ImplementationDisabled( 86 "Implementation '%s' is disabled." % impl_id 87 ) 88 else: 89 raise ImplementationNotFound(impl_id)
90
91 - def getFromFactory(self, factory_id, params):
92 """ 93 Get an implementation instance from the factory with ID ``factory_id`` 94 using the parameter dictionary ``params``. This is a shortcut which 95 binds to the factory and calls its :meth:`~FactoryInterface.get` method 96 then. 97 98 :exc:`~.InternalError` will be raised if required arguments are missing 99 in the ``params`` dictionary. :exc:`~.ImplementationDisabled` will be 100 raised if either the factory or the appropriate implementation are 101 disabled. :exc:`~.ImplementationNotFound` will be raised if either 102 the factory or the appropriate implementation are unknown to the 103 registry. 104 """ 105 106 factory = self.bind(factory_id) 107 108 return factory.get(**params)
109
110 - def findAndBind(self, intf_id, params):
111 """ 112 This method finds implementations based of a registered interface 113 with ID ``intf_id`` using the parameter dictionary ``params`` and 114 returns an instance of the matching implementation. This 115 works only for the ``kvp`` and ``testing`` binding methods, in other 116 cases :exc:`~.BindingMethodError` will be raised. 117 118 If the binding method of the interface is ``kvp`` the ``params`` 119 dictionary must map the registry keys defined in the interface 120 declaration to values. The KVP combination will be compared with the 121 values given in the respective implementations. If a matching 122 implementation is found an instance will be returned, otherwise 123 :exc:`~.ImplementationNotFound` is raised. If the class found is 124 disabled :exc:`~.ImplementationDisabled` is raised. 125 126 If the binding method of the interface is ``testing`` the ``params`` 127 dictionary will be passed to the :meth:`~TestingInterface.test` method 128 of the respective implementations. If no implementation matches 129 :exc:`~.ImplementationNotFound` will be raised. If more than one are 130 found :exc:`~.ImplementationAmbiguous` will be raised. 131 """ 132 133 if intf_id in self.__intf_index: 134 InterfaceCls = self.__intf_index[intf_id]["intf"] 135 if InterfaceCls.getBindingMethod() == "direct": 136 raise BindingMethodError("You have to bind directly to implementations of '%s'" % intf_id) 137 elif InterfaceCls.getBindingMethod() == "kvp": 138 ImplementationCls = self.__find_by_values( 139 InterfaceCls, params 140 ) 141 elif InterfaceCls.getBindingMethod() == "testing": 142 ImplementationCls = self.__find_by_test( 143 self.__intf_index[intf_id]["impls"], 144 params 145 ) 146 elif InterfaceCls.getBindingMethod() == "factory": 147 raise BindingMethodError("The registry cannot generate '%s' implementations. Use getFromFactory() instead." % intf_id) 148 149 return ImplementationCls() 150 else: 151 raise InternalError("Unknown interface ID '%s'" % intf_id)
152
153 - def findImplementations(self, intf_id, params=None, include_disabled=False):
154 """ 155 This method returns a list of implementations of a given interface. 156 It requires the interface ID as a parameter. 157 158 Furthermore, a parameter dictionary can be passed to the method. The 159 results will then be filtered according to these parameters. The 160 dictionary does not have to contain all the parameters defined by the 161 interface; in case some parameters are omitted, the result list may 162 contain several different implementations. 163 164 Third is an optional ``include_disabled`` parameter with defaults to 165 ``False``. If ``True``, disabled implementations will be reported as 166 well. 167 168 An :exc:`~.InternalError` is raised if parameters are passed to the 169 method that are not defined in the interface declaration or not 170 recognized by the interface :meth:`test` method. 171 """ 172 173 if intf_id in self.__intf_index: 174 InterfaceCls = self.__intf_index[intf_id]["intf"] 175 if params is not None: 176 _params = params 177 else: 178 _params = {} 179 180 if InterfaceCls.getBindingMethod() == "direct" or \ 181 InterfaceCls.getBindingMethod() == "factory": 182 impls = self.__find_all_impls( 183 self.__intf_index[intf_id]["impls"], 184 include_disabled 185 ) 186 elif InterfaceCls.getBindingMethod() == "kvp": 187 impls = self.__find_all_by_values( 188 self.__intf_index[intf_id]["impls"], 189 _params, 190 include_disabled 191 ) 192 elif InterfaceCls.getBindingMethod() == "testing": 193 impls = self.__find_all_by_test( 194 self.__intf_index[intf_id]["impls"], 195 _params, 196 include_disabled 197 ) 198 199 return impls 200 else: 201 raise InternalError("Unknown interface ID '%s'" % intf_id)
202
203 - def getImplementationIds(self, intf_id, params=None, include_disabled=False):
204 """ 205 This method returns a list of implementation IDs for a given interface. 206 It requires the interface ID as a parameter. 207 208 Furthermore, a parameter dictionary can be passed to the method. The 209 results will then be filtered according to these parameters. The 210 dictionary does not have to contain all the parameters defined by the 211 interface; in case some parameters are omitted, the result list may 212 contain several different implementations. 213 214 Third is an optional ``include_disabled`` parameter with defaults to 215 ``False``. If ``True``, disabled implementations will be reported as 216 well. 217 218 An :exc:`~.InternalError` is raised if parameters are passed to the 219 method that are not defined in the interface declaration or not 220 recognized by the interface :meth:`test` method. 221 """ 222 impls = self.findImplementations(intf_id, params, include_disabled) 223 224 return [impl.__get_impl_id__() for impl in impls]
225
226 - def getRegistryValues(self, intf_id, registry_key, filter=None, include_disabled=False):
227 """ 228 This method returns a list of registry values of implementations 229 of an interface with ``interface_id`` and a registry key 230 ``registry_key`` defined in the interface declaration. 231 232 With the ``filter`` argument you can impose certain restrictions on the 233 implementations (registry values) to be returned. It is expected to 234 contain a dictionary of registry keys and values that the implementation 235 must expose to be included. 236 237 Using the ``include_disabled`` argument you can determine whether. 238 239 This method raises :exc:`~.InternalError` if the interface ID is 240 unknown. 241 """ 242 if intf_id in self.__intf_index: 243 InterfaceCls = self.__intf_index[intf_id]["intf"] 244 245 if InterfaceCls.getBindingMethod() == "kvp": 246 if registry_key not in InterfaceCls.getRegistryKeys(): 247 raise InternalError("Interface '%s' has no registry key '%s'" % ( 248 intf_id, registry_key 249 )) 250 251 else: 252 if filter: 253 _filter = filter 254 else: 255 _filter = {} 256 257 impls = self.__find_all_by_values( 258 self.__intf_index[intf_id]["impls"], 259 _filter 260 ) 261 262 return [impl.__get_kvps__()[registry_key] for impl in impls] 263 else: 264 raise InternalError("Binding method of interface '%s' is '%s', not 'kvp'." % ( 265 intf_id, 266 InterfaceCls.getBindingMethod() 267 )) 268 else: 269 raise InternalError("Unknown interface ID '%s'" % intf_id)
270
271 - def getFactoryImplementations(self, factory):
272 """ 273 Returns a list of implementations for a given factory. 274 275 Raises :exc:`~.InternalError` if the factory is not found in the 276 registry. 277 """ 278 factory_id = factory.__get_impl_id__() 279 280 if factory_id in self.__fact_index: 281 return [entry["cls"] for entry in self.__fact_index[factory_id]] 282 else: 283 raise InternalError("Unknown Factory ID '%s'." % factory_id)
284
285 - def load(self):
286 """ 287 This method loads the registry, i.e. it scans the modules specified 288 in the configuration for interfaces and implementations. It is 289 invoked by the ``~.System`` class upon initialization. 290 291 You should *never* invoke this method directly. Always use 292 :meth:`~.System.init` to initialize and :meth:`~.System.getRegistry` to 293 access the registry. 294 295 There are three configuration settings taken into account. 296 297 First, the ``system_modules`` setting in ``default.conf``. These 298 modules are always loaded and cannot be left aside in individual 299 instances. 300 301 Second, the ``module_dirs`` setting in the local configuration of the 302 instance (``eoxserver.conf``) is taken into account. This expected to 303 be a comma-separated list of directories. These directories and all 304 the directory trees underneath them are searched for Python modules. 305 306 Third, the ``modules`` setting in ``eoxserver.conf``. This is 307 expecte to be a comma-separated list of module names which shall be 308 loaded. 309 310 All modules specified or detected by scanning directories will be 311 loaded and searched for interfaces descending from 312 :class:`RegisteredInterface` as well as their implementations. These 313 will be automatically included in the registry and accessible using 314 the different binding methods provided. 315 316 As a last step, the registry is synchronized with the database. This 317 means that the implementation looks up the entries for the different 318 implementations in the database and determines whether they are 319 enabled or not. If it finds an implementation which has not yet been 320 registered it will be saved to the database but disabled by default. 321 """ 322 # get module directories from config 323 reader = RegistryConfigReader(self.config) 324 reader.validate() 325 326 system_modules = reader.getSystemModules() 327 module_dirs = reader.getModuleDirectories() 328 modules = reader.getModules() 329 330 # find modules 331 for module_dir in module_dirs: 332 # find .py files 333 # and append them to the modules list 334 modules.extend(self.__find_modules(module_dir)) 335 336 # load modules 337 for module_name in system_modules: 338 self.__load_module(module_name, strict=True) 339 340 for module_name in modules: 341 self.__load_module(module_name) 342 343 self.__synchronize() 344 345 self.validate()
346
347 - def validate(self):
348 """ 349 This method is intended to validate the component configuration. 350 351 It looks up all implementations of :class:`ComponentManagerInterface` 352 and calls their respective :meth:`~ComponentManagerInterface.validate` 353 methods. 354 355 At the moment, no component managers are implemented, so this 356 method does not have any effects. 357 """ 358 msgs = [] 359 360 Managers = self.findImplementations("core.registry.ComponentManager") 361 362 for Manager in Managers: 363 manager = Manager() 364 try: 365 manager.validate(self) 366 except ConfigError, e: 367 msgs.append(str(e)) 368 369 if len(msgs) > 0: 370 raise ConfigError("\n".join(msgs))
371
372 - def save(self):
373 """ 374 This saves the registry configuration to the database. This means 375 the status of the enabled / disabled flag for each implementation 376 will be saved overriding any previous settings stored. 377 """ 378 self.validate() 379 380 self.__save_to_db()
381
382 - def getImplementationStatus(self, impl_id):
383 """ 384 Returns the implementation status (``True`` for enabled, ``False`` for 385 disabled) for the given implementation ID ``impl_id``. 386 387 Raises :exc:`~.InternalError` if the implementation ID is 388 unknown. 389 """ 390 if impl_id in self.__impl_index: 391 return self.__impl_index[impl_id]["enabled"] 392 else: 393 raise InternalError("Unknown implementation ID '%s'" % impl_id)
394
395 - def enableImplementation(self, impl_id):
396 """ 397 Changes the implementation status to enabled for implementation ID 398 ``impl_id``. Note that this change is not automatically stored to 399 the database (you have to call :meth:`save` to do that). 400 401 Raises :exc:`~.InternalError` if the implementation ID is unknown. 402 """ 403 if impl_id in self.__impl_index: 404 self.__impl_index[impl_id]["enabled"] = True 405 else: 406 raise InternalError("Unknown implementation ID '%s'" % impl_id)
407
408 - def disableImplementation(self, impl_id):
409 """ 410 Changed the implementation status to disable for implementation ID 411 ``impl_id``. Note that this change is not automatically stored to 412 the database (you have to call :meth:`save` to do that). 413 414 Raises :exc:`~.InternalError` if the implementation ID is unknown. 415 """ 416 if impl_id in self.__impl_index: 417 self.__impl_index[impl_id]["enabled"] = False 418 else: 419 raise InternalError("Unknown implementation ID '%s'" % impl_id)
420
421 - def clone(self):
422 """ 423 Returns an exact copy of the registry. 424 """ 425 426 registry = Registry(self.config) 427 428 registry.__impl_index = self.__impl_index 429 registry.__intf_index = self.__intf_index 430 registry.__kvp_index = self.__kvp_index 431 registry.__fact_index = self.__fact_index 432 433 return registry
434
435 - def __synchronize(self):
436 qs = self.__get_from_db() 437 438 for impl_id, entry in self.__impl_index.items(): 439 if entry["enabled"]: 440 logging.debug("%s: enabled" % impl_id) 441 else: 442 logging.debug("%s: disabled" % impl_id) 443 444 self.__save_diff_to_db(qs)
445
446 - def __get_from_db(self):
447 qs = Component.objects.all() 448 449 for comp in qs: 450 if comp.impl_id in self.__impl_index: 451 452 # TODO: at the moment, the 'enabled' property is stored 453 # in each index seperately. Make the indices point to 454 # the same entry, so 'enabled' does not need to be set 455 # three times (once for each index) 456 self.__impl_index[comp.impl_id]["enabled"] = comp.enabled 457 458 intf_id = self.__impl_index[comp.impl_id]["cls"].__get_intf_id__() 459 entries = self.__intf_index[intf_id]["impls"] 460 for entry in entries: 461 if entry["impl_id"] == comp.impl_id: 462 entry["enabled"] = comp.enabled 463 464 if self.__intf_index[intf_id]["intf"].getBindingMethod() == "kvp": 465 key = self.__make_kvp_index_key( 466 intf_id, 467 self.__intf_index[intf_id]["intf"].getRegistryKeys(), 468 self.__impl_index[comp.impl_id]["cls"].__get_kvps__() 469 ) 470 self.__kvp_index[key]["enabled"] = comp.enabled 471 472 return qs
473
474 - def __save_to_db(self, qs=None):
475 if qs: 476 _qs = qs 477 else: 478 _qs = Component.objects.all() 479 480 for comp in qs: 481 if comp.impl_id in self.__impl_index and\ 482 comp.enabled != self.__impl_index[comp.impl_id]["enabled"]: 483 comp.enabled = self.__impl_index[comp.impl_id]["enabled"] 484 comp.save() 485 486 self.__save_diff_to_db(_qs)
487
488 - def __save_diff_to_db(self, qs=None):
489 if qs is not None: 490 db_impl_ids = qs.values_list("impl_id", flat=True) 491 else: 492 db_impl_ids = Component.objects.all().values_list("impl_id", flat=True) 493 494 for impl_id in self.__impl_index.keys(): 495 if impl_id not in db_impl_ids: 496 Component.objects.create( 497 impl_id=impl_id, 498 intf_id=self.__impl_index[impl_id]["cls"].__get_intf_id__(), 499 enabled=self.__impl_index[impl_id]["enabled"] 500 )
501
502 - def __find_all_impls(self, entries, include_disabled=False):
503 if include_disabled: 504 return [entry["cls"] for entry in entries] 505 else: 506 return [entry["cls"] for entry in filter( 507 lambda entry: entry["enabled"], entries 508 )]
509
510 - def __find_by_values(self, InterfaceCls, kvp_dict, include_disabled=False):
511 key = self.__make_kvp_index_key( 512 InterfaceCls.getInterfaceId(), 513 InterfaceCls.getRegistryKeys(), 514 kvp_dict 515 ) 516 entry = self.__kvp_index.get(key) 517 518 if entry: 519 if entry["enabled"] or include_disabled: 520 return entry["cls"] 521 else: 522 raise ImplementationDisabled("Implementation '%s' is disabled." % entry["impl_id"]) 523 else: 524 raise ImplementationNotFound( 525 "No implementation found for interface '%s' and parameters: '%s'" % ( 526 InterfaceCls.getInterfaceId(), 527 str(kvp_dict) 528 ))
529
530 - def __find_all_by_values(self, entries, kvp_dict, include_disabled=False):
531 impls = [] 532 533 for entry in entries: 534 if entry["enabled"] or include_disabled: 535 impl_dict = entry["cls"].__get_kvps__() 536 matches = True 537 538 for key, value in kvp_dict.items(): 539 if key in impl_dict: 540 if value != impl_dict[key]: 541 matches = False 542 break 543 else: 544 raise InternalError("Key '%s' not found in registry values of implementation '%s'" % ( 545 key, entry["impl_id"] 546 )) 547 548 if matches: 549 impls.append(entry["cls"]) 550 551 return impls
552
553 - def __find_by_test(self, entries, test_params, include_disabled=False):
554 impls = self.__find_all_by_test(entries, test_params, include_disabled) 555 556 if len(impls) == 0: 557 raise ImplementationNotFound("") 558 elif len(impls) == 1: 559 return impls[0] 560 else: 561 raise ImplementationAmbiguous("")
562
563 - def __find_all_by_test(self, entries, test_params, include_disabled=False):
564 impls = [] 565 566 for entry in entries: 567 if entry["enabled"] or include_disabled: 568 if entry["cls"]().test(test_params): 569 impls.append(entry["cls"]) 570 571 return impls
572
573 - def __register_implementation(self, ImplementationCls):
574 # update interface index 575 self.__add_to_intf_index(ImplementationCls) 576 577 # update implementation index 578 self.__add_to_impl_index(ImplementationCls) 579 580 # update KVP index 581 self.__add_to_kvp_index(ImplementationCls) 582 583 # update factory index 584 self.__add_to_fact_index(ImplementationCls)
585
586 - def __register_interface(self, InterfaceCls):
587 logging.debug("Registry.__register_interface(): intf_id: %s" % InterfaceCls.getInterfaceId()) 588 589 intf_id = InterfaceCls.getInterfaceId() 590 591 if intf_id in self.__intf_index: 592 if InterfaceCls is not self.__intf_index[intf_id]["intf"]: 593 raise InternalError("Duplicate interface ID '%s' for '%s' in module '%s' and '%s' in module '%s'" % ( 594 intf_id, 595 InterfaceCls.__name__, 596 InterfaceCls.__module__, 597 self.__intf_index[intf_id]["intf"].__name__, 598 self.__intf_index[intf_id]["intf"].__module__ 599 )) 600 else: 601 self.__intf_index[intf_id] = { 602 "intf": InterfaceCls, 603 "impls": [] 604 }
605
606 - def __add_to_intf_index(self, ImplementationCls):
607 intf_id = ImplementationCls.__get_intf_id__() 608 impl_id = ImplementationCls.__get_impl_id__() 609 610 if intf_id in self.__intf_index: 611 if self.__intf_index[intf_id]["intf"] is ImplementationCls.__ifclass__: 612 self.__intf_index[intf_id]["impls"].append( 613 self.__get_index_entry(ImplementationCls) 614 ) 615 else: 616 raise InternalError("Duplicate Interface ID '%s' for '%s' in '%s' and '%s' in '%s'." % ( 617 intf_id, 618 ImplementationCls.__ifclass__.__name__, 619 ImplementationCls.__ifclass__.__module__, 620 self.__intf_index[intf_id]["intf"].__name__, 621 self.__intf_index[intf_id]["intf"].__module__ 622 )) 623 else: 624 self.__intf_index[intf_id] = { 625 "intf": ImplementationCls.__ifclass__, 626 "impls": [self.__get_index_entry(ImplementationCls)] 627 }
628
629 - def __add_to_impl_index(self, ImplementationCls):
630 intf_id = ImplementationCls.__get_intf_id__() 631 impl_id = ImplementationCls.__get_impl_id__() 632 633 if impl_id in self.__impl_index: 634 if self.__impl_index[impl_id] is not ImplementationCls: 635 raise InternalError("Duplicate implementation id '%s' defined in modules '%s' and '%s'" % ( 636 impl_id, 637 self.__impl_index[impl_id]["cls"].__module__, 638 ImplementationCls.__module__ 639 )) 640 else: 641 self.__impl_index[impl_id] = self.__get_index_entry(ImplementationCls)
642
643 - def __add_to_kvp_index(self, ImplementationCls):
644 645 intf_id = ImplementationCls.__get_intf_id__() 646 impl_id = ImplementationCls.__get_impl_id__() 647 648 if ImplementationCls.__ifclass__.getBindingMethod() == "kvp": 649 key = self.__make_kvp_index_key( 650 intf_id, 651 ImplementationCls.__ifclass__.getRegistryKeys(), 652 ImplementationCls.__get_kvps__() 653 ) 654 if key not in self.__kvp_index: 655 self.__kvp_index[key] = self.__get_index_entry(ImplementationCls) 656 else: 657 if self.__kvp_index[key] is not ImplementationCls: 658 # TODO: conflict resolution mechanisms 659 raise InternalError("Conflicting implementations for Interface '%s': Same KVP values for '%s' in '%s' and '%s' in '%s'" % ( 660 intf_id, 661 impl_id, 662 ImplementationCls.__module__, 663 self.__kvp_index[key]["cls"].__get_impl_id__(), 664 self.__kvp_index[key]["cls"].__module__ 665 ))
666
667 - def __add_to_fact_index(self, ImplementationCls):
668 if ImplementationCls.__ifclass__.getBindingMethod() == "factory": 669 670 671 for factory_id in ImplementationCls.__get_factory_ids__(): 672 logging.debug("Registry.__add_to_fact_index(): adding '%s' to '%s'" % ( 673 ImplementationCls.__get_impl_id__(), 674 factory_id 675 )) 676 677 if factory_id in self.__fact_index: 678 self.__fact_index[factory_id].append( 679 self.__get_index_entry(ImplementationCls) 680 ) 681 else: 682 self.__fact_index[factory_id] = [ 683 self.__get_index_entry(ImplementationCls) 684 ]
685
686 - def __get_index_entry(self, ImplementationCls, enabled=False):
687 return { 688 "cls": ImplementationCls, 689 "impl_id": ImplementationCls.__get_impl_id__(), 690 "enabled": enabled 691 }
692
693 - def __make_kvp_index_key(self, intf_id, registry_keys, kvp_dict):
694 key_list = [intf_id] 695 696 if registry_keys is None: 697 raise InternalError("Interface '%s' has no registry keys." % intf_id) 698 699 for registry_key in registry_keys: 700 if registry_key in kvp_dict: 701 key_list.append((kvp_dict[registry_key], registry_key)) 702 else: 703 raise InternalError("Missing registry key '%s'." % registry_key) 704 705 return tuple(key_list)
706
707 - def __find_modules(self, dir):
708 # check if dir is a subdirectory of the eoxserver or instance 709 # directory 710 abs_path = os.path.abspath(dir) 711 712 if abs_path.startswith(settings.PROJECT_DIR) or \ 713 abs_path.startswith(self.config.getEOxSPath()): 714 module_files = findFiles(abs_path, "*.py") 715 716 try: 717 return [pathToModuleName(module_file) for module_file in module_files] 718 except InternalError, e: 719 raise ConfigError(str(e)) 720 else: 721 raise ConfigError("Can search for extending modules and plugins in subdirecories of EOxServer distribution and instance dir only.")
722
723 - def __load_module(self, module_name, strict=False):
724 try: 725 # TODO: use imp module here (it reloads modules, which is more close to what we want) 726 module = __import__(module_name, globals(), locals(), []) 727 728 for sub_module_name in module_name.split(".")[1:]: 729 module = getattr(module, sub_module_name) 730 #f, path, desc = imp.find_module(module_name) 731 #module = imp.load_module(module_name, f, path, desc) 732 733 except Exception, e: 734 if strict: 735 raise InternalError("Could not load required module '%s'. Error was: %s" % ( 736 module_name, str(e) 737 )) 738 else: 739 # NOTE: a check for consistency will be applied later 740 # on; if an enabled component is missing, an exception 741 # will be raised by the final validation routine 742 logging.warning("Could not load module '%s'. Error was: %s" % ( 743 module_name, str(e) 744 )) 745 return 746 747 for attr in module.__dict__.values(): 748 if isclass(attr) and issubclass(attr, RegisteredInterface): 749 self.__register_interface(attr) 750 elif hasattr(attr, "__ifclass__") and\ 751 hasattr(attr, "__rconf__") and\ 752 hasattr(attr, "__get_intf_id__") and\ 753 hasattr(attr, "__get_impl_id__") and\ 754 hasattr(attr, "__get_kvps__"): 755 self.__register_implementation(attr)
756
757 -class RegisteredInterfaceMetaClass(InterfaceMetaClass):
758 - def __new__(cls, name, bases, class_dict):
759 if "REGISTRY_CONF" in class_dict: 760 local_conf = class_dict["REGISTRY_CONF"] 761 else: 762 raise InternalError("Every interface needs a 'REGISTRY_CONF' dictionary.") 763 764 cls._validateLocalRegistryConf(local_conf) 765 766 conf = cls._mergeConfs(local_conf, bases, "__rconf__") 767 768 cls._validateRegistryConf(conf) 769 770 class_dict["__rconf__"] = conf 771 772 return InterfaceMetaClass.__new__(cls, name, bases, class_dict)
773 774 @classmethod
775 - def _validateLocalRegistryConf(mcls, local_conf):
776 if "name" not in local_conf: 777 raise InternalError("Missing 'name' parameter in interface configuration dictionary.") 778 779 if "intf_id" not in local_conf: 780 raise InternalError("Missing 'intf_id' parameter in interface configuration dictionary.")
781 782 783 @classmethod
784 - def _validateRegistryConf(mcls, conf):
785 pass
786
787 -class RegisteredInterface(Interface):
788 """ 789 This class is the base class for all interfaces to be registered in the 790 registry. All interfaces whose implementations shall be registered must 791 be derived from :class:`RegisteredInterface`. 792 793 All interfaces derived from :class:`RegisteredInterface` must contain a 794 ``REGISTRY_CONF`` dictionary. See the introduction for details. 795 """ 796 797 __metaclass__ = RegisteredInterfaceMetaClass 798 799 REGISTRY_CONF = { 800 "name": "Abstract Registered Interface base class", 801 "intf_id": "core.registy.Registered", 802 "binding_method": "kvp" 803 } 804 805 @classmethod
806 - def _getClassDict(InterfaceCls, ImplementationCls, bases):
807 class_dict = super(RegisteredInterface, InterfaceCls)._getClassDict(ImplementationCls, bases) 808 809 if hasattr(ImplementationCls, "REGISTRY_CONF"): 810 conf = ImplementationCls.REGISTRY_CONF 811 else: 812 raise InternalError("Missing 'REGISTRY_CONF' configuration dictionary in implementing class '%s'." % ImplementationCls.__name__) 813 814 InterfaceCls._validateImplementationConf(conf) 815 816 intf_id = InterfaceCls.getInterfaceId() 817 impl_id = conf["impl_id"] 818 kvps = conf.get("registry_values") 819 factory_ids = conf.get("factory_ids", ()) 820 821 class_dict.update({ 822 "__rconf__": conf, 823 "__get_intf_id__": classmethod(lambda cls: intf_id), 824 "__get_impl_id__": classmethod(lambda cls: impl_id), 825 "__get_kvps__": classmethod(lambda cls: kvps), 826 "__get_factory_ids__": classmethod(lambda cls: factory_ids) 827 }) 828 829 return class_dict
830 831 @classmethod
832 - def _validateImplementationConf(InterfaceCls, conf):
833 if "name" not in conf: 834 raise InternalError("Missing 'name' parameter in implementation configuration dictionary.") 835 836 if "impl_id" not in conf: 837 raise InternalError("Missing 'impl_id' parameter in implementation configuration dictionary.") 838 839 if InterfaceCls.getBindingMethod() == "kvp": 840 if "registry_values" not in conf: 841 raise InternalError("Missing 'registry_values' parameter in implementation configuration dictionary.") 842 843 if not InterfaceCls._keysMatch(conf["registry_values"].keys(), InterfaceCls.getRegistryKeys()): 844 raise InternalError("Registry keys in implementation configuration dictionary for '%s' do not match interface definition" % conf["impl_id"])
845 846 @classmethod
847 - def _keysMatch(InterfaceCls, impl_keys, intf_keys):
848 return (len(impl_keys) == 0 and len(intf_keys) == 0) or\ 849 (all(map(lambda key: key in intf_keys, impl_keys)) and \ 850 all(map(lambda key: key in impl_keys, intf_keys)))
851 852 @classmethod
853 - def getInterfaceId(cls):
854 return cls.__rconf__["intf_id"]
855 856 @classmethod
857 - def getBindingMethod(cls):
858 return cls.__rconf__["binding_method"]
859 860 @classmethod
861 - def getRegistryKeys(cls):
862 if "registry_keys" in cls.__rconf__: 863 return cls.__rconf__["registry_keys"] 864 else: 865 return []
866
867 -class TestingInterface(RegisteredInterface):
868 """ 869 This class is a descendant of :class:`RegisteredInterface` that adds 870 a single method. It is used for binding by test, which enables binding 871 decisions that cannot easily be implemented by key-value-pair comparisons. 872 873 .. method:: test(params) 874 875 This method is invoked by the registry when determining which 876 implementation to bind to. Based on the parameter dictionary ``params`` 877 the method shall decide whether the implementation is applicable and 878 return ``True``. If it is not applicable the method shall return 879 ``False``. 880 """ 881 REGISTRY_CONF = { 882 "name": "Registered Testing Interface", 883 "intf_id": "core.registry.Testing", 884 "binding_method": "testing" 885 } 886 887 test = Method( 888 DictArg("params"), 889 returns=BoolArg("@return") 890 )
891
892 -class FactoryInterface(RegisteredInterface):
893 """ 894 This is the basic interface for factories. It is a descendant of 895 :class:`RegisteredInterface`. 896 897 .. method:: get(**kwargs) 898 899 This method shall return an instance of an implementation that matches 900 the parameters given as keyword arguments. The set of arguments 901 understood depends on the individual factory and can be found in the 902 respective documentation. 903 904 The method shall raise an exception if no matching implementation or 905 instance thereof can be found, or if the choice is ambiguous. 906 907 .. method:: find(**kwargs) 908 909 This method shall return a list of implementation instances that 910 matches the parameters given as keyword arguments. The set of arguments 911 understood depends on the individual factory and can be found in the 912 respective documentation. 913 """ 914 915 REGISTRY_CONF = { 916 "name": "Registered Factory Interface", 917 "intf_id": "core.registry.Factory", 918 "binding_method": "direct" 919 } 920 921 get = Method( 922 KwArgs("kwargs"), 923 returns=Arg("@return") 924 ) 925 926 find = Method( 927 KwArgs("kwargs"), 928 returns=ListArg("@return") 929 )
930
931 -class ComponentManagerInterface(RegisteredInterface):
932 """ 933 This interface is not in use at the moment. It was intended to provide 934 an API for controlling the status of a larger set of implementations and 935 their dependencies, though the concept has never been elaborated. 936 """ 937 REGISTRY_CONF = { 938 "name": "Component Manager Interface", 939 "intf_id": "core.registry.ComponentManager", 940 "binding_method": "direct" 941 } 942 943 getName = Method( 944 returns=StringArg("@returns") 945 ) 946 947 getId = Method( 948 returns=StringArg("@returns") 949 ) 950 951 enable = Method( 952 ObjectArg("registry", arg_class=Registry), 953 BoolArg("cascade", default=False) 954 ) 955 956 disable = Method( 957 ObjectArg("registry", arg_class=Registry), 958 BoolArg("cascade", default=False) 959 ) 960 961 createRelations = Method() 962 963 enableRelation = Method( 964 StringArg("obj_id") 965 ) 966 967 disableRelation = Method( 968 StringArg("obj_id") 969 ) 970 971 notify = Method( 972 ObjectArg("resource"), 973 StringArg("event") 974 )
975
976 -class RegistryConfigReader(object):
977 """ 978 This class provides some functions for reading configuration settings used 979 by the :class:`Registry`. 980 """
981 - def __init__(self, config):
982 self.config = config
983
984 - def validate(self):
985 """ 986 Validates the configuration; a no-op at the moment. 987 """ 988 pass
989
990 - def getSystemModules(self):
991 """ 992 This method returns a list of dotted names of system modules. The 993 values are read from ``system_modules`` setting in the 994 ``[core.registry]`` section of the ``default.conf`` configuration file. 995 996 The format of the setting is expected to be a comma-separated list of 997 the module names. 998 """ 999 sys_mod_str = self.config.getDefaultConfigValue("core.registry", "system_modules") 1000 1001 if sys_mod_str: 1002 return [name.strip() for name in sys_mod_str.split(",")] 1003 else: 1004 return []
1005
1006 - def getModuleDirectories(self):
1007 """ 1008 This method returns a list of directory paths where to look for 1009 modules to load (see also :meth:`Registry.load`). The values are read 1010 from the ``module_dirs`` setting in the ``[core.registry]`` section of 1011 the instance specific ``eoxserver.conf`` configuration file. 1012 1013 The format of the setting is expected to be a comma-separated list of 1014 paths. 1015 """ 1016 mod_dir_str = self.config.getConfigValue("core.registry", "module_dirs") 1017 1018 if mod_dir_str: 1019 return [dir.strip() for dir in mod_dir_str.split(",")] 1020 else: 1021 return []
1022
1023 - def getModules(self):
1024 """ 1025 This method returs a list of dotted names of modules to be loaded (see 1026 also :meth:`Registry.load`). The values are read from the ``modules`` 1027 setting in the ``[core.registry]`` section of the instance specific 1028 ``eoxserver.conf`` configuration file. 1029 1030 The format of the setting is expected to be a comma-separated list of 1031 dotted module names. 1032 """ 1033 mod_str = self.config.getConfigValue("core.registry", "modules") 1034 1035 if mod_str: 1036 return [name.strip() for name in mod_str.split(",")] 1037 else: 1038 return []
1039