Plugins¶
EOxServer uses a plugin framework to extend or alter the built-in
functionality. The plugin system is based on trac’s Component Architecture. We copied the
relevant file as eoxserver.core.component
to not add the full trac
framework as a dependency.
EOxServer plugins are classes that inherit from
eoxserver.core.component.Component
. Each component can implement any
number of interfaces, which are usually skeleton classes to provide
documentation of what methods and fields each implementation shall provide.
In this architecture, interfaces are just informative and allow the runtime
binding via eoxserver.core.component.ExtensionPoint
.
All plugins are self-registering, which means the module containing the component just needs to be imported through any kind of import mechanism and, voilà, the component is registered and ready for use.
Important¶
Components should not be created manually, but only be retrieved via an
eoxserver.core.component.ExtensionPoint
. This further implies that
the __init__()
method shall not take any arguments, as instance creation
is out of the reach.
Additionally, Component
instances are never destroyed and shared
among different threads, so it is highly advised to not store any data in the
Component
itself.
Loading modules¶
EOxServer provides mechanisms to conveniently load modules and thus
registering all entailed plugins. This is done via the COMPONENTS
setting in your instances settings.py
.
This setting must be an iterable of strings which follow the dotted python module path notation, with two exceptions:
- Module paths ending with “.*” will import all modules of a package.
- Paths ending with “.**” will do the same, with the exception of doing so recursively.
E.g: "eoxserver.services.ows.**"
will load all subpackages and modules of the eoxserver.services.ows
package. (This is an easy way to enable
all OWS services, by the way).
To only enable WMS in version 1.3 you could use the following import line:
"eoxserver.services.ows.wms.v13.*"
. If you only want to only enable
specific requests (for whatever reason) you’d have to list their modules
seperately.
The EOxServer instance settings.py
template is already preconfigured
with the most common components modules.
Example¶
The following demonstrates the use of the component architecture in a simplified manner:
In myapp/interfaces.py
:
class DataReaderInterface(object):
"Interface for reading data from a file."
def read_data(self, filename, n):
"Read 'n' bytes from the file 'filename'."
In myapp/components.py
:
from eoxserver.core.component import Component, implements
from myapp.interfaces import DataReaderInterface
class BasicDataReader(Component):
"Reads data from the file with the built-in Python functionality."
implements(DataReaderInterface)
def read_data(self, filename, n):
with open(filename) as f:
return f.read(n)
We can now use this component the following way in myapp/main.py
:
from myapp.interfaces import DataReaderInterface
class App(object):
data_readers = ExtensionPoint(DataReaderInterface)
def run(self, filename):
if not self.data_readers:
raise Exception("No data reader implementation found.")
print(data_readers[0].read_data(filename))
In the “myapp/interfaces.py” we declare an interface for “data readers”. The
only method implementations of this interface shall provide is the
read_data()
method. In the “myapp/components.py” we provide a simple
implementation of this interface that uses built-in functionality to open a
file and read a data. Please not the implements(DataReaderInterface) which
declares that this component implements a specific interface.
In the “myapp/main.py” we declare a class that actually tries to find an
implementation of the DataReaderInterface
and invoke its
read_data()
method. In this case we only use the first available
implementation of the interface, in other cases it might make sense to loop
over all, or search for a specific one that satisfies a condition.