source: TI05-delivery/ows_framework/branches/ows_framework-refactor/ows_common/ows_common/pylons/ows_controller.py @ 3487

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI05-delivery/ows_framework/branches/ows_framework-refactor/ows_common/ows_common/pylons/ows_controller.py@3487
Revision 3487, 7.3 KB checked in by spascoe, 12 years ago (diff)
Line 
1"""
2Base controller for OGC Web Services (OWS).
3
4@author: Stephen Pascoe
5@todo: Add pluggable security so replicate what was previously implemented
6    for the NDG discovery portal in ows_server.lib.BaseController.
7"""
8
9
10from pylons import request, response, config, c
11from pylons.controllers import WSGIController
12from pylons.templating import render
13from webhelpers import url_for
14
15from ows_common import exceptions as OWS_E
16from ows_common.util import negotiate_version, check_updatesequence
17from ows_common.builder import loadConfigFile
18from ows_common import helpers
19from ows_common.operations_metadata import *
20from ows_common.contents import Contents
21
22from genshi.template import TemplateLoader
23from pkg_resources import resource_filename
24
25try:
26    from xml.etree import ElementTree as ET
27except ImportError:
28    from elementtree import ElementTree as ET
29
30import logging
31logger = logging.getLogger(__name__)
32
33# Instantiate Genshi template loader
34genshiLoader = TemplateLoader(
35    resource_filename('ows_common.pylons', 'templates'),
36    auto_reload=True,
37    )
38
39# Configure
40#!TODO: rename this configuration object to something non-NDG specific
41#EXCEPTION_TYPE = request.environ['ndgConfig'].get('OWS_SERVER', 'exception_type', 'ogc').lower()
42EXCEPTION_TYPE = config.get('ows_server.exception_type', 'ogc')
43
44
45
46class OwsControllerBase(WSGIController):
47    """
48    @ivar owsParams: A dictionary of parameters passed to the service.
49        Initially these comes from the query string but could come from
50        a HTTP POST in future.
51    @cvar owsOperations: A list of operation names
52   
53    """
54
55    owsOperations = []
56   
57    def __call__(self, environ, start_response):
58
59        self._loadOwsParams()
60
61        # If the EXCEPTION_TYPE is 'pylons' let Pylons catch any exceptions.
62        # Otherwise send an OGC exception report for any OWS_E.OwsError
63        if 'pylons' in EXCEPTION_TYPE:
64            self._fixOwsAction(environ)
65            return super(OwsControllerBase, self).__call__(environ, start_response)
66        else:
67            try:
68                self._fixOwsAction(environ)
69                return super(OwsControllerBase, self).__call__(environ, start_response)
70            except OWS_E.OwsError, e:
71                logger.exception(e)
72
73                tmpl = genshiLoader.load('exception_report.xml')
74                response.write(tmpl.generate(report=e.report).render('xml'))
75                response.headers['content-type'] = 'text/xml'
76                return response
77
78    def _loadOwsParams(self):
79        # All OWS parameter names are case insensitive.
80        self._owsParams = {}
81        for k in request.params:
82            self._owsParams[k.lower()] = request.params[k]
83
84    def _fixOwsAction(self, environ):
85        rdict = environ['pylons.routes_dict']
86       
87        # Override the Routes action from the request query parameter
88        action = self.getOwsParam('request')
89
90        # Check action is a method in self and is defined as an OWS operation
91        if action not in self.owsOperations:
92            raise OWS_E.InvalidParameterValue('request=%s not supported' % action,
93                                              'REQUEST')
94        rdict['action'] = action
95
96    def getOwsParam(self, param, **kwargs):
97        """
98        Returns the value of a OWS parameter passed to the operation.
99        If argv['default'] is given it is taken to be the default
100        value otherwise the parameter is treated as manditory and an
101        exception is raised if the parameter is not present.
102
103        """
104        try:
105            return self._owsParams[param.lower()]
106        except KeyError:
107            if 'default' in kwargs:
108                return kwargs['default']
109            else:
110                raise OWS_E.MissingParameterValue('%s parameter is not specified' % param,
111                                                  param)
112
113#-----------------------------------------------------------------------------
114# Functions that populate c.capabilities
115
116def addOperation(opName, formats=[]):
117    ops = c.capabilities.operationsMetadata.operationDict
118    ops[opName] = helpers.operation(url_for(), formats=formats)
119
120def addLayer(name, title, abstract, srs, bbox):
121    if c.capabilities.contents is None:
122        c.capabilities.contents = Contents()
123
124    layer = helpers.wms_layer(url_for(), name, title, srs, bbox, abstract)
125    c.capabilities.contents.datasetSummaries.append(layer)
126
127def initCapabilities():
128    """
129    Initialise the capabilities object c.capabilities.
130
131    By default the server-wide configuration file is loaded and
132    used to populate some standard metadata.  The GetCapabilites
133    operation is added.
134
135    """
136    # Load the basic ServiceMetadata from a config file
137    configFile = config.get('ows_server.capabilities_config')
138    c.capabilities = loadConfigFile(configFile)
139
140    om = OperationsMetadata(operationDict={})
141    c.capabilities.operationsMetadata = om
142
143    addOperation('GetCapabilities', formats=['text/xml'])
144
145#-----------------------------------------------------------------------------
146
147class OwsController(OwsControllerBase):
148    """
149    Adds basic GetCapabilities response to OwsControllerBase.
150
151    @cvar service: If None does not enforce the SERVICE parameter.  Otherwise
152        raises exception if SERVICE is not correct on GetCapabilities request.
153    @cvar validVersions: A list of supported version numbers.  Automatic
154        version negotiation is performed according to this attribute.
155   
156    @ivar updateSequence: None if cache-control is not supported or an
157        updateSequence identifier.  This attribute should be set in the
158        controller's __before__() method.
159    """
160
161    owsOperations = ['GetCapabilities']
162
163    # Override these attributes to control how OwsController responds to
164    # GetCapabilities
165    service = None
166    validVersions = NotImplemented
167
168    # To enable cache control set this instance attribute in self.__before__().
169    updateSequence = None
170   
171    def GetCapabilities(self):
172
173        # Retrieve Operation parameters
174        service = self.getOwsParam('service')
175        version = self.getOwsParam('version', default=None)
176        format = self.getOwsParam('format', default='text/xml')
177        updateSequence = self.getOwsParam('updatesequence', default=None)
178
179        # Check update sequence
180        check_updatesequence(self.updateSequence, updateSequence)
181
182        # Do version negotiation
183        version = negotiate_version(self.validVersions, version)
184
185        # Get information required for the capabilities document
186        initCapabilities()
187        self._loadCapabilities()
188       
189        # Render the capabilities document       
190        response.headers['content-type'] = format
191        return self._renderCapabilities(version, format)
192
193
194    def _loadCapabilities(self):
195        """
196        Override in subclasses to populate c.capabilities with
197        operation and contents metadata.
198
199        """
200        pass
201
202    def _renderCapabilities(self, version, format):
203        """
204        Override in subclases to render the capabilities document.
205        The response mime-type will already have been set.  Raise an
206        OWS exception if the format is not supported.
207
208        @param version: the version as a string
209        @param format: the format as a string
210       
211        @return: a template as expected by pylons.render()
212        """
213        raise NotImplementedError
214
215   
Note: See TracBrowser for help on using the repository browser.