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.