org.deegree.services.wfs.WebFeatureService.java Source code

Java tutorial

Introduction

Here is the source code for org.deegree.services.wfs.WebFeatureService.java

Source

//$HeadURL: svn+ssh://mschneider@svn.wald.intevation.org/deegree/deegree3/services/trunk/src/org/deegree/services/controller/wps/WPSController.java $
/*----------------------------------------------------------------------------
 This file is part of deegree, http://deegree.org/
 Copyright (C) 2001-2012 by:
 Department of Geography, University of Bonn
 and
 lat/lon GmbH
    
 This library is free software; you can redistribute it and/or modify it under
 the terms of the GNU Lesser General Public License as published by the Free
 Software Foundation; either version 2.1 of the License, or (at your option)
 any later version.
 This library 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 Lesser General Public License for more
 details.
 You should have received a copy of the GNU Lesser General Public License
 along with this library; if not, write to the Free Software Foundation, Inc.,
 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    
 Contact information:
    
 lat/lon GmbH
 Aennchenstr. 19, 53177 Bonn
 Germany
 http://lat-lon.de/
    
 Department of Geography, University of Bonn
 Prof. Dr. Klaus Greve
 Postfach 1147, 53001 Bonn
 Germany
 http://www.geographie.uni-bonn.de/deegree/
    
 e-mail: info@deegree.org
 ----------------------------------------------------------------------------*/
package org.deegree.services.wfs;

import static org.deegree.commons.ows.exception.OWSException.INVALID_PARAMETER_VALUE;
import static org.deegree.commons.ows.exception.OWSException.NO_APPLICABLE_CODE;
import static org.deegree.commons.ows.exception.OWSException.OPERATION_NOT_SUPPORTED;
import static org.deegree.commons.utils.StringUtils.REMOVE_DOUBLE_FIELDS;
import static org.deegree.commons.utils.StringUtils.REMOVE_EMPTY_FIELDS;
import static org.deegree.gml.GMLVersion.GML_2;
import static org.deegree.gml.GMLVersion.GML_30;
import static org.deegree.gml.GMLVersion.GML_31;
import static org.deegree.gml.GMLVersion.GML_32;
import static org.deegree.protocol.wfs.WFSConstants.VERSION_100;
import static org.deegree.protocol.wfs.WFSConstants.VERSION_110;
import static org.deegree.protocol.wfs.WFSConstants.VERSION_200;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.dom.DOMSource;

import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMException;
import org.apache.axiom.soap.SOAP11Version;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.commons.fileupload.FileItem;
import org.deegree.commons.ows.exception.OWSException;
import org.deegree.commons.ows.metadata.DatasetMetadata;
import org.deegree.commons.ows.metadata.MetadataUrl;
import org.deegree.commons.ows.metadata.ServiceIdentification;
import org.deegree.commons.ows.metadata.ServiceProvider;
import org.deegree.commons.tom.ows.CodeType;
import org.deegree.commons.tom.ows.LanguageString;
import org.deegree.commons.tom.ows.Version;
import org.deegree.commons.utils.Pair;
import org.deegree.commons.utils.StringUtils;
import org.deegree.commons.utils.kvp.InvalidParameterValueException;
import org.deegree.commons.utils.kvp.KVPUtils;
import org.deegree.commons.utils.kvp.MissingParameterException;
import org.deegree.commons.xml.XMLAdapter;
import org.deegree.commons.xml.XMLParsingException;
import org.deegree.commons.xml.stax.SchemaLocationXMLStreamWriter;
import org.deegree.commons.xml.stax.XMLStreamUtils;
import org.deegree.cs.CRSUtils;
import org.deegree.cs.coordinatesystems.ICRS;
import org.deegree.cs.persistence.CRSManager;
import org.deegree.feature.persistence.FeatureStore;
import org.deegree.feature.types.FeatureType;
import org.deegree.gml.GMLVersion;
import org.deegree.protocol.ows.getcapabilities.GetCapabilities;
import org.deegree.protocol.ows.getcapabilities.GetCapabilitiesKVPParser;
import org.deegree.protocol.wfs.WFSRequestType;
import org.deegree.protocol.wfs.capabilities.GetCapabilitiesXMLAdapter;
import org.deegree.protocol.wfs.describefeaturetype.DescribeFeatureType;
import org.deegree.protocol.wfs.describefeaturetype.kvp.DescribeFeatureTypeKVPAdapter;
import org.deegree.protocol.wfs.describefeaturetype.xml.DescribeFeatureTypeXMLAdapter;
import org.deegree.protocol.wfs.getfeature.GetFeature;
import org.deegree.protocol.wfs.getfeature.kvp.GetFeatureKVPAdapter;
import org.deegree.protocol.wfs.getfeature.xml.GetFeatureXMLAdapter;
import org.deegree.protocol.wfs.getfeaturewithlock.GetFeatureWithLock;
import org.deegree.protocol.wfs.getfeaturewithlock.kvp.GetFeatureWithLockKVPAdapter;
import org.deegree.protocol.wfs.getfeaturewithlock.xml.GetFeatureWithLockXMLAdapter;
import org.deegree.protocol.wfs.getgmlobject.GetGmlObject;
import org.deegree.protocol.wfs.getgmlobject.kvp.GetGmlObjectKVPAdapter;
import org.deegree.protocol.wfs.getgmlobject.xml.GetGmlObjectXMLAdapter;
import org.deegree.protocol.wfs.getpropertyvalue.GetPropertyValue;
import org.deegree.protocol.wfs.getpropertyvalue.kvp.GetPropertyValueKVPAdapter;
import org.deegree.protocol.wfs.getpropertyvalue.xml.GetPropertyValueXMLAdapter;
import org.deegree.protocol.wfs.lockfeature.LockFeature;
import org.deegree.protocol.wfs.lockfeature.kvp.LockFeatureKVPAdapter;
import org.deegree.protocol.wfs.lockfeature.xml.LockFeatureXMLAdapter;
import org.deegree.protocol.wfs.storedquery.CreateStoredQuery;
import org.deegree.protocol.wfs.storedquery.DescribeStoredQueries;
import org.deegree.protocol.wfs.storedquery.DropStoredQuery;
import org.deegree.protocol.wfs.storedquery.ListStoredQueries;
import org.deegree.protocol.wfs.storedquery.kvp.DescribeStoredQueriesKVPAdapter;
import org.deegree.protocol.wfs.storedquery.kvp.DropStoredQueryKVPAdapter;
import org.deegree.protocol.wfs.storedquery.kvp.ListStoredQueriesKVPAdapter;
import org.deegree.protocol.wfs.storedquery.xml.CreateStoredQueryXMLAdapter;
import org.deegree.protocol.wfs.storedquery.xml.DescribeStoredQueriesXMLAdapter;
import org.deegree.protocol.wfs.storedquery.xml.DropStoredQueryXMLAdapter;
import org.deegree.protocol.wfs.storedquery.xml.ListStoredQueriesXMLAdapter;
import org.deegree.protocol.wfs.transaction.Transaction;
import org.deegree.protocol.wfs.transaction.action.IDGenMode;
import org.deegree.protocol.wfs.transaction.kvp.TransactionKVPAdapter;
import org.deegree.protocol.wfs.transaction.xml.TransactionXmlReader;
import org.deegree.protocol.wfs.transaction.xml.TransactionXmlReaderFactory;
import org.deegree.services.OWS;
import org.deegree.services.OWSProvider;
import org.deegree.services.controller.AbstractOWS;
import org.deegree.services.controller.ImplementationMetadata;
import org.deegree.services.controller.OGCFrontController;
import org.deegree.services.controller.exception.serializer.XMLExceptionSerializer;
import org.deegree.services.controller.utils.HttpResponseBuffer;
import org.deegree.services.i18n.Messages;
import org.deegree.services.jaxb.controller.DeegreeServiceControllerType;
import org.deegree.services.jaxb.metadata.DeegreeServicesMetadataType;
import org.deegree.services.jaxb.wfs.AbstractFormatType;
import org.deegree.services.jaxb.wfs.CustomFormat;
import org.deegree.services.jaxb.wfs.DeegreeWFS;
import org.deegree.services.jaxb.wfs.DeegreeWFS.EnableTransactions;
import org.deegree.services.jaxb.wfs.DeegreeWFS.ExtendedCapabilities;
import org.deegree.services.jaxb.wfs.DeegreeWFS.SupportedVersions;
import org.deegree.services.jaxb.wfs.FeatureTypeMetadata;
import org.deegree.services.jaxb.wfs.GMLFormat;
import org.deegree.services.jaxb.wfs.IdentifierGenerationOptionType;
import org.deegree.services.metadata.MetadataUtils;
import org.deegree.services.metadata.OWSMetadataProvider;
import org.deegree.services.metadata.provider.DefaultOWSMetadataProvider;
import org.deegree.services.metadata.provider.OWSMetadataProviderProvider;
import org.deegree.services.ows.OWS100ExceptionReportSerializer;
import org.deegree.services.ows.OWS110ExceptionReportSerializer;
import org.deegree.services.ows.PreOWSExceptionReportSerializer;
import org.deegree.services.wfs.format.Format;
import org.deegree.services.wfs.query.StoredQueryHandler;
import org.deegree.workspace.ResourceIdentifier;
import org.deegree.workspace.ResourceInitException;
import org.deegree.workspace.ResourceMetadata;
import org.deegree.workspace.Workspace;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;

/**
 * Implementation of the <a href="http://www.opengeospatial.org/standards/wfs">OpenGIS Web Feature Service</a> server
 * protocol.
 * <p>
 * Supported WFS protocol versions:
 * <ul>
 * <li>1.0.0</li>
 * <li>1.1.0</li>
 * <li>2.0.0</li>
 * </ul>
 * </p>
 * 
 * @see AbstractOWS
 * @see OGCFrontController
 * 
 * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider</a>
 * @author last edited by: $Author: mschneider $
 * 
 * @version $Revision: 15339 $, $Date: 2008-12-11 18:40:09 +0100 (Do, 11 Dez 2008) $
 */
public class WebFeatureService extends AbstractOWS {

    private static final Logger LOG = LoggerFactory.getLogger(WebFeatureService.class);

    private static final int DEFAULT_MAX_FEATURES = 15000;

    private WfsFeatureStoreManager service;

    private LockFeatureHandler lockFeatureHandler;

    private StoredQueryHandler storedQueryHandler;

    private boolean enableTransactions;

    private IDGenMode idGenMode;

    private boolean disableBuffering = true;

    private ICRS defaultQueryCRS = CRSUtils.EPSG_4326;

    private List<ICRS> queryCRS = new ArrayList<ICRS>();

    private final Map<String, Format> mimeTypeToFormat = new LinkedHashMap<String, Format>();

    private final Map<GMLVersion, Format> gmlVersionToFormat = new HashMap<GMLVersion, Format>();

    private int queryMaxFeatures;

    private boolean checkAreaOfUse;

    private OWSMetadataProvider mdProvider;

    public WebFeatureService(ResourceMetadata<OWS> metadata, Workspace workspace, Object jaxbConfig) {
        super(metadata, workspace, jaxbConfig);
    }

    @Override
    public void init(DeegreeServicesMetadataType serviceMetadata, DeegreeServiceControllerType mainConf,
            Object controllerConf) {

        LOG.info("Initializing WFS.");

        DeegreeWFS jaxbConfig = (DeegreeWFS) controllerConf;
        initOfferedVersions(jaxbConfig.getSupportedVersions());

        EnableTransactions enableTransactions = jaxbConfig.getEnableTransactions();
        if (enableTransactions != null) {
            this.enableTransactions = enableTransactions.isValue();
            this.idGenMode = parseIdGenMode(enableTransactions.getIdGen());
        }
        if (jaxbConfig.isEnableResponseBuffering() != null) {
            disableBuffering = !jaxbConfig.isEnableResponseBuffering();
        } else if (jaxbConfig.isDisableResponseBuffering() != null) {
            disableBuffering = jaxbConfig.isDisableResponseBuffering();
        }

        queryMaxFeatures = jaxbConfig.getQueryMaxFeatures() == null ? DEFAULT_MAX_FEATURES
                : jaxbConfig.getQueryMaxFeatures().intValue();
        checkAreaOfUse = jaxbConfig.isQueryCheckAreaOfUse() == null ? false : jaxbConfig.isQueryCheckAreaOfUse();

        service = new WfsFeatureStoreManager();
        try {
            service.init(jaxbConfig, workspace);
        } catch (Exception e) {
            throw new ResourceInitException("Error initializing WFS/FeatureStores: " + e.getMessage(), e);
        }

        lockFeatureHandler = new LockFeatureHandler(this);
        List<URL> list = new ArrayList<URL>();
        for (String file : jaxbConfig.getStoredQuery()) {
            URL url = metadata.getLocation().resolveToUrl(file);
            if (url == null) {
                LOG.warn("Could not resolve {}.", file);
            } else {
                list.add(url);
            }
        }
        storedQueryHandler = new StoredQueryHandler(this, list);

        initQueryCRS(jaxbConfig.getQueryCRS());
        initFormats(jaxbConfig.getAbstractFormat());
        mdProvider = initMetadataProvider(serviceMetadata, jaxbConfig);
    }

    private IDGenMode parseIdGenMode(IdentifierGenerationOptionType idGen) {
        if (idGen == null) {
            return IDGenMode.GENERATE_NEW;
        }
        switch (idGen) {
        case GENERATE_NEW: {
            return IDGenMode.GENERATE_NEW;
        }
        case USE_EXISTING: {
            return IDGenMode.USE_EXISTING;
        }
        case REPLACE_DUPLICATE: {
            return IDGenMode.REPLACE_DUPLICATE;
        }
        }
        return null;
    }

    private String getMetadataURL(String metadataUrlTemplate, FeatureTypeMetadata ftMd) {
        if (metadataUrlTemplate == null || ftMd == null || ftMd.getMetadataSetId() == null) {
            return null;
        }
        return StringUtils.replaceAll(metadataUrlTemplate, "${metadataSetId}", ftMd.getMetadataSetId());
    }

    private void initOfferedVersions(SupportedVersions supportedVersions) {

        List<String> versions = null;
        if (supportedVersions != null) {
            versions = supportedVersions.getVersion();
        }
        if (versions == null || versions.isEmpty()) {
            LOG.info("No protocol versions specified. Activating all implemented versions.");
            ImplementationMetadata<?> md = ((OWSProvider) getMetadata().getProvider()).getImplementationMetadata();
            versions = new ArrayList<String>(md.getImplementedVersions().size());
            for (Version version : md.getImplementedVersions()) {
                versions.add(version.toString());
            }
        }
        validateAndSetOfferedVersions(versions);
    }

    private void initQueryCRS(List<String> queryCRSLists) {
        // try {
        for (String queryCRS : queryCRSLists) {
            String[] querySrs = StringUtils.split(queryCRS, " ", REMOVE_EMPTY_FIELDS | REMOVE_DOUBLE_FIELDS);
            for (String srs : querySrs) {
                LOG.debug("Query CRS: " + srs);
                ICRS crs = CRSManager.getCRSRef(srs);
                this.queryCRS.add(crs);
            }
        }
        // } catch ( UnknownCRSException e ) {
        // String msg = "Invalid QuerySRS parameter: " + e.getMessage();
        // throw new ControllerInitException( msg );
        // }
        if (queryCRS.isEmpty()) {
            LOG.info("No query CRS defined, defaulting to EPSG:4326.");
            queryCRS.add(CRSUtils.EPSG_4326);
        }
        defaultQueryCRS = this.queryCRS.get(0);
    }

    private void initFormats(List<JAXBElement<? extends AbstractFormatType>> formatList) {

        if (formatList == null || formatList.isEmpty()) {
            LOG.debug("Using default format configuration.");
            org.deegree.services.wfs.format.gml.GmlFormat gml21 = new org.deegree.services.wfs.format.gml.GmlFormat(
                    this, GML_2);
            org.deegree.services.wfs.format.gml.GmlFormat gml30 = new org.deegree.services.wfs.format.gml.GmlFormat(
                    this, GML_30);
            org.deegree.services.wfs.format.gml.GmlFormat gml31 = new org.deegree.services.wfs.format.gml.GmlFormat(
                    this, GML_31);
            org.deegree.services.wfs.format.gml.GmlFormat gml32 = new org.deegree.services.wfs.format.gml.GmlFormat(
                    this,

                    GML_32);
            mimeTypeToFormat.put("application/gml+xml; version=2.1", gml21);
            mimeTypeToFormat.put("application/gml+xml; version=3.0", gml30);
            mimeTypeToFormat.put("application/gml+xml; version=3.1", gml31);
            mimeTypeToFormat.put("application/gml+xml; version=3.2", gml32);
            mimeTypeToFormat.put("text/xml; subtype=gml/2.1.2", gml21);
            mimeTypeToFormat.put("text/xml; subtype=gml/3.0.1", gml30);
            mimeTypeToFormat.put("text/xml; subtype=gml/3.1.1", gml31);
            mimeTypeToFormat.put("text/xml; subtype=gml/3.2.1", gml32);
            mimeTypeToFormat.put("text/xml; subtype=\"gml/2.1.2\"", gml21);
            mimeTypeToFormat.put("text/xml; subtype=\"gml/3.0.1\"", gml30);
            mimeTypeToFormat.put("text/xml; subtype=\"gml/3.1.1\"", gml31);
            mimeTypeToFormat.put("text/xml; subtype=\"gml/3.2.1\"", gml32);
        } else {
            LOG.debug("Using customized format configuration.");
            for (JAXBElement<? extends AbstractFormatType> formatEl : formatList) {
                AbstractFormatType formatDef = formatEl.getValue();
                List<String> mimeTypes = formatDef.getMimeType();
                Format format = null;
                if (formatDef instanceof GMLFormat) {
                    format = new org.deegree.services.wfs.format.gml.GmlFormat(this, (GMLFormat) formatDef);
                } else if (formatDef instanceof CustomFormat) {
                    CustomFormat cf = (CustomFormat) formatDef;
                    String className = cf.getJavaClass();
                    LOG.info("Using custom format class '" + className + "'.");
                    try {
                        format = (org.deegree.services.wfs.format.CustomFormat) Class.forName(className)
                                .newInstance();
                        ((org.deegree.services.wfs.format.CustomFormat) format).init(this, cf.getConfig());
                    } catch (Exception e) {
                        throw new ResourceInitException("Error initializing WFS format: " + e.getMessage(), e);
                    }
                } else {
                    throw new ResourceInitException(
                            "Internal error. Unhandled AbstractFormatType '" + formatDef.getClass() + "'.");
                }
                for (String mimeType : mimeTypes) {
                    mimeTypeToFormat.put(mimeType, format);
                }
            }
        }

        for (Format f : mimeTypeToFormat.values()) {
            if (f instanceof org.deegree.services.wfs.format.gml.GmlFormat) {
                gmlVersionToFormat.put(
                        ((org.deegree.services.wfs.format.gml.GmlFormat) f).getGmlFormatOptions().getGmlVersion(),
                        f);
            }
        }
    }

    private OWSMetadataProvider initMetadataProvider(DeegreeServicesMetadataType serviceMetadata,
            DeegreeWFS jaxbConfig) throws ResourceInitException {
        OWSMetadataProvider provider = null;
        provider = workspace.getResource(OWSMetadataProviderProvider.class,
                getMetadata().getIdentifier().getId() + "_metadata");

        if (provider == null) {
            ServiceIdentification serviceId = MetadataUtils
                    .convertFromJAXB(serviceMetadata.getServiceIdentification());
            if (serviceId.getTitles().isEmpty()) {
                serviceId.setTitles(Collections.singletonList(new LanguageString("deegree 3 WFS", null)));
            }
            if (serviceId.getAbstracts().isEmpty()) {
                serviceId.setAbstracts(Collections.singletonList(new LanguageString("deegree 3 WFS", null)));
            }
            ServiceProvider serviceProvider = MetadataUtils.convertFromJAXB(serviceMetadata.getServiceProvider());

            if (serviceProvider.getProviderName() == null) {
                serviceProvider.setProviderName("deegree organization");
            }
            if (serviceProvider.getProviderSite() == null) {
                serviceProvider.setProviderSite("http://www.deegree.org");
            }

            List<DatasetMetadata> ftMetadata = new ArrayList<DatasetMetadata>();
            String metadataUrlTemplate = jaxbConfig.getMetadataURLTemplate();
            if (metadataUrlTemplate == null) {
                // use local CSW (if running)
                List<ResourceIdentifier<OWS>> owss = workspace.getResourcesOfType(OWSProvider.class);
                for (ResourceIdentifier<OWS> id : owss) {
                    OWS o = workspace.getResource(OWSProvider.class, id.getId());
                    if (o != null) {
                        ImplementationMetadata<?> md = ((OWSProvider) o.getMetadata().getProvider())
                                .getImplementationMetadata();
                        for (String s : md.getImplementedServiceName()) {
                            if (s.equalsIgnoreCase("csw")) {
                                metadataUrlTemplate = OGCFrontController.getHttpGetURL();
                                if (!metadataUrlTemplate.endsWith("?")) {
                                    metadataUrlTemplate = metadataUrlTemplate + "?";
                                }
                                metadataUrlTemplate += "service=CSW&request=GetRecordById&version=2.0.2&outputSchema=http://www.isotc211.org/2005/gmd&elementSetName=full&id=${metadataSetId}";
                            }
                        }
                    }
                }
            }

            for (FeatureTypeMetadata ftMd : jaxbConfig.getFeatureTypeMetadata()) {
                // TODO
                List<LanguageString> titles = null;
                // TODO
                List<LanguageString> abstracts = null;
                // TODO
                List<Pair<List<LanguageString>, CodeType>> keywords = null;
                final List<MetadataUrl> metadataUrls = new ArrayList<MetadataUrl>();
                final String url = getMetadataURL(metadataUrlTemplate, ftMd);
                if (url != null) {
                    metadataUrls.add(new MetadataUrl(url, null, null));
                }
                try {
                    DatasetMetadata dsMd = new DatasetMetadata(ftMd.getName(), titles, abstracts, keywords,
                            metadataUrls, null, null, null, null);
                    ftMetadata.add(dsMd);
                } catch (Throwable t) {
                    t.printStackTrace();
                }

            }

            Map<String, List<OMElement>> wfsVersionToExtendedCaps = new HashMap<String, List<OMElement>>();
            List<ExtendedCapabilities> extendedCapConfigs = jaxbConfig.getExtendedCapabilities();
            if (extendedCapConfigs != null) {
                for (ExtendedCapabilities extendedCapConfig : extendedCapConfigs) {
                    Element extendedCaps = extendedCapConfig.getAny();
                    DOMSource domSource = new DOMSource(extendedCaps);
                    XMLStreamReader xmlStream;
                    try {
                        xmlStream = XMLInputFactory.newInstance().createXMLStreamReader(domSource);
                    } catch (Exception t) {
                        throw new ResourceInitException("Error extracting extended capabilities: " + t.getMessage(),
                                t);
                    }
                    OMElement omEl = new XMLAdapter(xmlStream).getRootElement();
                    for (String wfsVersion : extendedCapConfig.getWfsVersions()) {
                        if (wfsVersionToExtendedCaps.containsKey(wfsVersion)) {
                            String msg = "Multiple ExtendedCapabilities sections for WFS version: " + wfsVersion
                                    + ".";
                            throw new ResourceInitException(msg);
                        }
                        wfsVersionToExtendedCaps.put(wfsVersion, Collections.singletonList(omEl));
                    }
                }
            }
            provider = new DefaultOWSMetadataProvider(serviceId, serviceProvider, wfsVersionToExtendedCaps,
                    ftMetadata, Collections.<String, String>emptyMap(), null);
        }
        return provider;
    }

    @Override
    public void destroy() {
        LOG.debug("destroy");
    }

    /**
     * Returns the underlying {@link WfsFeatureStoreManager} instance.
     * 
     * @return the underlying {@link WfsFeatureStoreManager}
     */
    public WfsFeatureStoreManager getStoreManager() {
        return service;
    }

    /**
     * @return the stored query handler for this service, never <code>null</code>.
     */
    public StoredQueryHandler getStoredQueryHandler() {
        return storedQueryHandler;
    }

    @Override
    public void doKVP(Map<String, String> kvpParamsUC, HttpServletRequest request, HttpResponseBuffer response,
            List<FileItem> multiParts) throws ServletException, IOException {

        LOG.debug("doKVP");
        Version requestVersion = null;
        try {
            requestVersion = getVersion(kvpParamsUC.get("VERSION"));
            String requestName = KVPUtils.getRequired(kvpParamsUC, "REQUEST");
            WFSRequestType requestType = getRequestTypeByName(requestName);

            // check if requested version is supported and offered (except for GetCapabilities)
            if (requestType != WFSRequestType.GetCapabilities) {
                if (requestVersion == null) {
                    throw new OWSException("Missing version parameter.", OWSException.MISSING_PARAMETER_VALUE,
                            "version");
                }

                checkVersion(requestVersion);
            }

            // needed for CITE 1.1.0 compliance
            if (requestVersion != null && requestVersion.equals(VERSION_110)) {
                String serviceAttr = KVPUtils.getRequired(kvpParamsUC, "SERVICE");
                if (!"WFS".equals(serviceAttr)) {
                    throw new OWSException("Wrong service attribute: '" + serviceAttr + "' -- must be 'WFS'.",
                            OWSException.INVALID_PARAMETER_VALUE, "service");
                }
            }

            // build namespaces from NamespaceHints given in the configuration
            Map<String, String> nsMap = service.getPrefixToNs();

            if (disableBuffering) {
                response.disableBuffering();
            }

            switch (requestType) {
            case CreateStoredQuery:
                throw new OWSException(Messages.get("WFS_NO_KVP_BINDING", requestName, requestVersion),
                        OPERATION_NOT_SUPPORTED);
            case DescribeFeatureType:
                DescribeFeatureType describeFt = DescribeFeatureTypeKVPAdapter.parse(kvpParamsUC);
                Format format = determineFormat(requestVersion, describeFt.getOutputFormat(), "outputFormat");
                format.doDescribeFeatureType(describeFt, response, false);
                break;
            case DescribeStoredQueries:
                DescribeStoredQueries describeStoredQueries = DescribeStoredQueriesKVPAdapter.parse(kvpParamsUC);
                storedQueryHandler.doDescribeStoredQueries(describeStoredQueries, response);
                break;
            case DropStoredQuery:
                DropStoredQuery dropStoredQuery = DropStoredQueryKVPAdapter.parse(kvpParamsUC);
                storedQueryHandler.doDropStoredQuery(dropStoredQuery, response);
                break;
            case GetCapabilities:
                GetCapabilities getCapabilities = GetCapabilitiesKVPParser.parse(kvpParamsUC);
                doGetCapabilities(getCapabilities, response);
                break;
            case GetFeature:
                GetFeature getFeature = GetFeatureKVPAdapter.parse(kvpParamsUC, nsMap);
                format = determineFormat(requestVersion, getFeature.getPresentationParams().getOutputFormat(),
                        "outputFormat");
                format.doGetFeature(getFeature, response);
                break;
            case GetFeatureWithLock:
                checkTransactionsEnabled(requestName);
                GetFeatureWithLock getFeatureWithLock = GetFeatureWithLockKVPAdapter.parse(kvpParamsUC);
                format = determineFormat(requestVersion,
                        getFeatureWithLock.getPresentationParams().getOutputFormat(), "outputFormat");
                format.doGetFeature(getFeatureWithLock, response);
                break;
            case GetGmlObject:
                GetGmlObject getGmlObject = GetGmlObjectKVPAdapter.parse(kvpParamsUC);
                format = determineFormat(requestVersion, getGmlObject.getOutputFormat(), "outputFormat");
                format.doGetGmlObject(getGmlObject, response);
                break;
            case GetPropertyValue:
                GetPropertyValue getPropertyValue = GetPropertyValueKVPAdapter.parse(kvpParamsUC);
                format = determineFormat(requestVersion, getPropertyValue.getPresentationParams().getOutputFormat(),
                        "outputFormat");
                format.doGetPropertyValue(getPropertyValue, response);
                break;
            case ListStoredQueries:
                ListStoredQueries listStoredQueries = ListStoredQueriesKVPAdapter.parse(kvpParamsUC);
                storedQueryHandler.doListStoredQueries(listStoredQueries, response);
                break;
            case LockFeature:
                checkTransactionsEnabled(requestName);
                LockFeature lockFeature = LockFeatureKVPAdapter.parse(kvpParamsUC);
                lockFeatureHandler.doLockFeature(lockFeature, response);
                break;
            case Transaction:
                if (requestVersion.equals(VERSION_200)) {
                    throw new OWSException(Messages.get("WFS_NO_KVP_BINDING", requestName, requestVersion),
                            OPERATION_NOT_SUPPORTED);
                }
                checkTransactionsEnabled(requestName);
                Transaction transaction = TransactionKVPAdapter.parse(kvpParamsUC);
                new TransactionHandler(this, service, transaction, idGenMode).doTransaction(response);
                break;
            default:
                throw new RuntimeException("Internal error: Unhandled request '" + requestName + "'.");
            }
        } catch (OWSException e) {
            LOG.debug("OWS-Exception: {}", e.getMessage());
            LOG.trace(e.getMessage(), e);
            sendServiceException(requestVersion, e, response);
        } catch (MissingParameterException e) {
            LOG.debug("OWS-Exception: {}", e.getMessage());
            LOG.trace(e.getMessage(), e);
            sendServiceException(requestVersion, new OWSException(e), response);
        } catch (InvalidParameterValueException e) {
            LOG.debug("OWS-Exception: {}", e.getMessage());
            LOG.trace(e.getMessage(), e);
            sendServiceException(requestVersion, new OWSException(e), response);
        } catch (Exception e) {
            LOG.debug("OWS-Exception: {}", e.getMessage());
            LOG.trace(e.getMessage(), e);
            sendServiceException(requestVersion, new OWSException(e.getMessage(), NO_APPLICABLE_CODE), response);
        }
    }

    private void checkTransactionsEnabled(String requestName) throws OWSException {
        if (!enableTransactions) {
            throw new OWSException(Messages.get("WFS_TRANSACTIONS_DISABLED", requestName),
                    OWSException.OPERATION_NOT_SUPPORTED);
        }
    }

    @Override
    public void doXML(XMLStreamReader xmlStream, HttpServletRequest request, HttpResponseBuffer response,
            List<FileItem> multiParts) throws ServletException, IOException {

        LOG.debug("doXML");
        Version requestVersion = null;
        try {
            String requestName = xmlStream.getLocalName();
            WFSRequestType requestType = getRequestTypeByName(requestName);

            // check if requested version is supported and offered (except for GetCapabilities)
            requestVersion = getVersion(XMLStreamUtils.getAttributeValue(xmlStream, "version"));
            if (requestType != WFSRequestType.GetCapabilities) {
                requestVersion = checkVersion(requestVersion);

                // needed for CITE 1.1.0 compliance
                String serviceAttr = XMLStreamUtils.getAttributeValue(xmlStream, "service");
                if (serviceAttr != null && !("WFS".equals(serviceAttr) || "".equals(serviceAttr))) {
                    throw new OWSException("Wrong service attribute: '" + serviceAttr + "' -- must be 'WFS'.",
                            INVALID_PARAMETER_VALUE, "service");
                }
            }

            if (disableBuffering) {
                response.disableBuffering();
            }

            switch (requestType) {
            case CreateStoredQuery:
                CreateStoredQueryXMLAdapter createStoredQueryAdapter = new CreateStoredQueryXMLAdapter();
                createStoredQueryAdapter.setRootElement(new XMLAdapter(xmlStream).getRootElement());
                CreateStoredQuery createStoredQuery = createStoredQueryAdapter.parse();
                storedQueryHandler.doCreateStoredQuery(createStoredQuery, response);
                break;
            case DescribeFeatureType:
                DescribeFeatureTypeXMLAdapter describeFtAdapter = new DescribeFeatureTypeXMLAdapter();
                describeFtAdapter.setRootElement(new XMLAdapter(xmlStream).getRootElement());
                DescribeFeatureType describeFt = describeFtAdapter.parse();
                Format format = determineFormat(requestVersion, describeFt.getOutputFormat(), "outputFormat");
                format.doDescribeFeatureType(describeFt, response, false);
                break;
            case DropStoredQuery:
                DropStoredQueryXMLAdapter dropStoredQueryAdapter = new DropStoredQueryXMLAdapter();
                dropStoredQueryAdapter.setRootElement(new XMLAdapter(xmlStream).getRootElement());
                DropStoredQuery dropStoredQuery = dropStoredQueryAdapter.parse();
                storedQueryHandler.doDropStoredQuery(dropStoredQuery, response);
                break;
            case DescribeStoredQueries:
                DescribeStoredQueriesXMLAdapter describeStoredQueriesAdapter = new DescribeStoredQueriesXMLAdapter();
                describeStoredQueriesAdapter.setRootElement(new XMLAdapter(xmlStream).getRootElement());
                DescribeStoredQueries describeStoredQueries = describeStoredQueriesAdapter.parse();
                storedQueryHandler.doDescribeStoredQueries(describeStoredQueries, response);
                break;
            case GetCapabilities:
                GetCapabilitiesXMLAdapter getCapabilitiesAdapter = new GetCapabilitiesXMLAdapter();
                getCapabilitiesAdapter.setRootElement(new XMLAdapter(xmlStream).getRootElement());
                GetCapabilities wfsRequest = getCapabilitiesAdapter.parse(requestVersion);
                doGetCapabilities(wfsRequest, response);
                break;
            case GetFeature:
                GetFeatureXMLAdapter getFeatureAdapter = new GetFeatureXMLAdapter();
                getFeatureAdapter.setRootElement(new XMLAdapter(xmlStream).getRootElement());
                GetFeature getFeature = getFeatureAdapter.parse();
                format = determineFormat(requestVersion, getFeature.getPresentationParams().getOutputFormat(),
                        "outputFormat");
                format.doGetFeature(getFeature, response);
                break;
            case GetFeatureWithLock:
                checkTransactionsEnabled(requestName);
                GetFeatureWithLockXMLAdapter getFeatureWithLockAdapter = new GetFeatureWithLockXMLAdapter();
                getFeatureWithLockAdapter.setRootElement(new XMLAdapter(xmlStream).getRootElement());
                GetFeatureWithLock getFeatureWithLock = getFeatureWithLockAdapter.parse();
                format = determineFormat(requestVersion,
                        getFeatureWithLock.getPresentationParams().getOutputFormat(), "outputFormat");
                format.doGetFeature(getFeatureWithLock, response);
                break;
            case GetGmlObject:
                GetGmlObjectXMLAdapter getGmlObjectAdapter = new GetGmlObjectXMLAdapter();
                getGmlObjectAdapter.setRootElement(new XMLAdapter(xmlStream).getRootElement());
                GetGmlObject getGmlObject = getGmlObjectAdapter.parse();
                format = determineFormat(requestVersion, getGmlObject.getOutputFormat(), "outputFormat");
                format.doGetGmlObject(getGmlObject, response);
                break;
            case GetPropertyValue:
                GetPropertyValueXMLAdapter getPropertyValueAdapter = new GetPropertyValueXMLAdapter();
                getPropertyValueAdapter.setRootElement(new XMLAdapter(xmlStream).getRootElement());
                GetPropertyValue getPropertyValue = getPropertyValueAdapter.parse();
                format = determineFormat(requestVersion, getPropertyValue.getPresentationParams().getOutputFormat(),
                        "outputFormat");
                format.doGetPropertyValue(getPropertyValue, response);
                break;
            case ListStoredQueries:
                ListStoredQueriesXMLAdapter listStoredQueriesAdapter = new ListStoredQueriesXMLAdapter();
                listStoredQueriesAdapter.setRootElement(new XMLAdapter(xmlStream).getRootElement());
                ListStoredQueries listStoredQueries = listStoredQueriesAdapter.parse();
                storedQueryHandler.doListStoredQueries(listStoredQueries, response);
                break;
            case LockFeature:
                checkTransactionsEnabled(requestName);
                LockFeatureXMLAdapter lockFeatureAdapter = new LockFeatureXMLAdapter();
                lockFeatureAdapter.setRootElement(new XMLAdapter(xmlStream).getRootElement());
                LockFeature lockFeature = lockFeatureAdapter.parse();
                lockFeatureHandler.doLockFeature(lockFeature, response);
                break;
            case Transaction:
                checkTransactionsEnabled(requestName);
                TransactionXmlReader transactionReader = new TransactionXmlReaderFactory().createReader(xmlStream);
                Transaction transaction = transactionReader.read(xmlStream);
                new TransactionHandler(this, service, transaction, idGenMode).doTransaction(response);
                break;
            default:
                throw new RuntimeException("Internal error: Unhandled request '" + requestName + "'.");
            }
        } catch (OWSException e) {
            LOG.debug(e.getMessage(), e);
            sendServiceException(requestVersion, e, response);
        } catch (XMLParsingException e) {
            LOG.trace("Stack trace:", e);
            sendServiceException(requestVersion, new OWSException(e.getMessage(), INVALID_PARAMETER_VALUE),
                    response);
        } catch (MissingParameterException e) {
            LOG.trace("Stack trace:", e);
            sendServiceException(requestVersion, new OWSException(e), response);
        } catch (InvalidParameterValueException e) {
            LOG.trace("Stack trace:", e);
            sendServiceException(requestVersion, new OWSException(e), response);
        } catch (Throwable e) {
            LOG.trace("Stack trace:", e);
            sendServiceException(requestVersion, new OWSException(e.getMessage(), NO_APPLICABLE_CODE), response);
        }
    }

    @Override
    public void doSOAP(SOAPEnvelope soapDoc, HttpServletRequest request, HttpResponseBuffer response,
            List<FileItem> multiParts, SOAPFactory factory)
            throws ServletException, IOException, org.deegree.services.authentication.SecurityException {
        LOG.debug("doSOAP");

        if (disableBuffering) {
            super.doSOAP(soapDoc, request, response, multiParts, factory);
            return;
        }

        Version requestVersion = null;
        try {
            if (soapDoc.getVersion() instanceof SOAP11Version) {
                response.setContentType("application/soap+xml");
                XMLStreamWriter xmlWriter = response.getXMLWriter();
                String soapEnvNS = "http://schemas.xmlsoap.org/soap/envelope/";
                String xsiNS = "http://www.w3.org/2001/XMLSchema-instance";
                xmlWriter.writeStartElement("soap", "Envelope", soapEnvNS);
                xmlWriter.writeNamespace("soap", soapEnvNS);
                xmlWriter.writeNamespace("xsi", xsiNS);
                xmlWriter.writeAttribute(xsiNS, "schemaLocation",
                        "http://schemas.xmlsoap.org/soap/envelope/ http://schemas.xmlsoap.org/soap/envelope/");
                xmlWriter.writeStartElement(soapEnvNS, "Body");
            } else {
                beginSOAPResponse(response);
            }

            OMElement body = soapDoc.getBody().getFirstElement().cloneOMElement();
            XMLStreamReader bodyXmlStream = body.getXMLStreamReaderWithoutCaching();

            String requestName = body.getLocalName();
            WFSRequestType requestType = getRequestTypeByName(requestName);

            // check if requested version is supported and offered (except for GetCapabilities)
            requestVersion = getVersion(body.getAttributeValue(new QName("version")));
            if (requestType != WFSRequestType.GetCapabilities) {
                requestVersion = checkVersion(requestVersion);

                // needed for CITE 1.1.0 compliance
                String serviceAttr = body.getAttributeValue(new QName("service"));
                if (serviceAttr != null && !("WFS".equals(serviceAttr) || "".equals(serviceAttr))) {
                    throw new OWSException("Wrong service attribute: '" + serviceAttr + "' -- must be 'WFS'.",
                            INVALID_PARAMETER_VALUE, "service");
                }
            }

            switch (requestType) {
            case CreateStoredQuery:
                CreateStoredQueryXMLAdapter createStoredQueryAdapter = new CreateStoredQueryXMLAdapter();
                createStoredQueryAdapter.setRootElement(body);
                CreateStoredQuery createStoredQuery = createStoredQueryAdapter.parse();
                storedQueryHandler.doCreateStoredQuery(createStoredQuery, response);
                break;
            case DescribeFeatureType:
                DescribeFeatureTypeXMLAdapter describeFtAdapter = new DescribeFeatureTypeXMLAdapter();
                describeFtAdapter.setRootElement(body);
                DescribeFeatureType describeFt = describeFtAdapter.parse();
                Format format = determineFormat(requestVersion, describeFt.getOutputFormat(), "outputFormat");
                format.doDescribeFeatureType(describeFt, response, true);
                break;
            case DropStoredQuery:
                DropStoredQueryXMLAdapter dropStoredQueryAdapter = new DropStoredQueryXMLAdapter();
                dropStoredQueryAdapter.setRootElement(body);
                DropStoredQuery dropStoredQuery = dropStoredQueryAdapter.parse();
                storedQueryHandler.doDropStoredQuery(dropStoredQuery, response);
                break;
            case DescribeStoredQueries:
                DescribeStoredQueriesXMLAdapter describeStoredQueriesAdapter = new DescribeStoredQueriesXMLAdapter();
                describeStoredQueriesAdapter.setRootElement(body);
                DescribeStoredQueries describeStoredQueries = describeStoredQueriesAdapter.parse();
                storedQueryHandler.doDescribeStoredQueries(describeStoredQueries, response);
                break;
            case GetCapabilities:
                GetCapabilitiesXMLAdapter getCapabilitiesAdapter = new GetCapabilitiesXMLAdapter();
                getCapabilitiesAdapter.setRootElement(body);
                GetCapabilities wfsRequest = getCapabilitiesAdapter.parse(requestVersion);
                doGetCapabilities(wfsRequest, response);
                break;
            case GetFeature:
                GetFeatureXMLAdapter getFeatureAdapter = new GetFeatureXMLAdapter();
                getFeatureAdapter.setRootElement(body);
                GetFeature getFeature = getFeatureAdapter.parse();
                format = determineFormat(requestVersion, getFeature.getPresentationParams().getOutputFormat(),
                        "outputFormat");
                format.doGetFeature(getFeature, response);
                break;
            case GetFeatureWithLock:
                checkTransactionsEnabled(requestName);
                GetFeatureWithLockXMLAdapter getFeatureWithLockAdapter = new GetFeatureWithLockXMLAdapter();
                getFeatureWithLockAdapter.setRootElement(body);
                GetFeatureWithLock getFeatureWithLock = getFeatureWithLockAdapter.parse();
                format = determineFormat(requestVersion,
                        getFeatureWithLock.getPresentationParams().getOutputFormat(), "outputFormat");
                format.doGetFeature(getFeatureWithLock, response);
                break;
            case GetGmlObject:
                GetGmlObjectXMLAdapter getGmlObjectAdapter = new GetGmlObjectXMLAdapter();
                getGmlObjectAdapter.setRootElement(body);
                GetGmlObject getGmlObject = getGmlObjectAdapter.parse();
                format = determineFormat(requestVersion, getGmlObject.getOutputFormat(), "outputFormat");
                format.doGetGmlObject(getGmlObject, response);
                break;
            case GetPropertyValue:
                GetPropertyValueXMLAdapter getPropertyValueAdapter = new GetPropertyValueXMLAdapter();
                getPropertyValueAdapter.setRootElement(body);
                GetPropertyValue getPropertyValue = getPropertyValueAdapter.parse();
                format = determineFormat(requestVersion, getPropertyValue.getPresentationParams().getOutputFormat(),
                        "outputFormat");
                format.doGetPropertyValue(getPropertyValue, response);
                break;
            case ListStoredQueries:
                ListStoredQueriesXMLAdapter listStoredQueriesAdapter = new ListStoredQueriesXMLAdapter();
                listStoredQueriesAdapter.setRootElement(body);
                ListStoredQueries listStoredQueries = listStoredQueriesAdapter.parse();
                storedQueryHandler.doListStoredQueries(listStoredQueries, response);
                break;
            case LockFeature:
                checkTransactionsEnabled(requestName);
                LockFeatureXMLAdapter lockFeatureAdapter = new LockFeatureXMLAdapter();
                lockFeatureAdapter.setRootElement(body);
                LockFeature lockFeature = lockFeatureAdapter.parse();
                lockFeatureHandler.doLockFeature(lockFeature, response);
                break;
            case Transaction:
                checkTransactionsEnabled(requestName);
                TransactionXmlReader transactionReader = new TransactionXmlReaderFactory()
                        .createReader(requestVersion);
                Transaction transaction = transactionReader.read(bodyXmlStream);
                new TransactionHandler(this, service, transaction, idGenMode).doTransaction(response);
                break;
            default:
                throw new RuntimeException("Internal error: Unhandled request '" + requestName + "'.");
            }

            endSOAPResponse(response);

        } catch (OWSException e) {
            LOG.debug(e.getMessage(), e);
            sendSoapException(soapDoc, factory, response, e, request, requestVersion);
        } catch (XMLParsingException e) {
            LOG.trace("Stack trace:", e);
            sendSoapException(soapDoc, factory, response, new OWSException(e.getMessage(), INVALID_PARAMETER_VALUE),
                    request, requestVersion);
        } catch (MissingParameterException e) {
            LOG.trace("Stack trace:", e);
            sendSoapException(soapDoc, factory, response, new OWSException(e), request, requestVersion);
        } catch (InvalidParameterValueException e) {
            LOG.trace("Stack trace:", e);
            sendSoapException(soapDoc, factory, response, new OWSException(e), request, requestVersion);
        } catch (Throwable e) {
            LOG.trace("Stack trace:", e);
            sendSoapException(soapDoc, factory, response, new OWSException(e.getMessage(), NO_APPLICABLE_CODE),
                    request, requestVersion);
        }

    }

    private Version getVersion(String versionString) throws OWSException {
        Version version = null;
        if (versionString != null && !"".equals(versionString)) {
            try {
                version = Version.parseVersion(versionString);
            } catch (InvalidParameterValueException e) {
                throw new OWSException(e.getMessage(), OWSException.INVALID_PARAMETER_VALUE, "version");
            }
        }
        return version;
    }

    private WFSRequestType getRequestTypeByName(String requestName) throws OWSException {
        ImplementationMetadata<?> md = ((OWSProvider) getMetadata().getProvider()).getImplementationMetadata();
        WFSRequestType requestType = (WFSRequestType) md.getRequestTypeByName(requestName);
        if (requestType == null) {
            String msg = "Request type '" + requestName + "' is not supported.";
            throw new OWSException(msg, OWSException.OPERATION_NOT_SUPPORTED, "request");
        }
        return requestType;
    }

    private void doGetCapabilities(GetCapabilities request, HttpResponseBuffer response)
            throws XMLStreamException, IOException, OWSException {

        LOG.debug("doGetCapabilities: " + request);
        Version negotiatedVersion = negotiateVersion(request);

        // cope with the 'All' section specifier
        Set<String> sections = request.getSections();
        Set<String> sectionsUC = new HashSet<String>();
        for (String section : sections) {
            if (section.equalsIgnoreCase("ALL")) {
                sectionsUC = null;
                break;
            }
            sectionsUC.add(section.toUpperCase());
        }
        // never empty (only null)
        if (sectionsUC != null && sectionsUC.size() == 0) {
            sectionsUC = null;
        }
        final Collection<FeatureType> sortedFts = getFeatureTypesToExport();

        XMLStreamWriter xmlWriter = getXMLResponseWriter(response, "text/xml", null);
        GetCapabilitiesHandler adapter = new GetCapabilitiesHandler(this, service, negotiatedVersion, xmlWriter,
                sortedFts, sectionsUC, enableTransactions, queryCRS, mdProvider);
        adapter.export();
        xmlWriter.flush();
    }

    private Collection<FeatureType> getFeatureTypesToExport() {
        if (mdProvider.getDatasetMetadata() != null && !mdProvider.getDatasetMetadata().isEmpty()) {
            LOG.debug("Dataset metadata available. Only announcing feature types with metadata.");
            return getFeatureTypesWithMetadata();
        }
        LOG.debug("No dataset metadata available. Announcing feature types from all feature stores.");
        return getAllFeatureTypes();
    }

    private Collection<FeatureType> getFeatureTypesWithMetadata() {
        final Collection<FeatureType> sortedFts = new LinkedHashSet<FeatureType>();
        for (final DatasetMetadata datasetMetadata : mdProvider.getDatasetMetadata()) {
            final QName ftName = datasetMetadata.getQName();
            final FeatureStore fs = service.getStore(ftName);
            if (fs != null) {
                if (fs.isMapped(ftName)) {
                    sortedFts.add(service.lookupFeatureType(ftName));
                }
            } else {
                LOG.warn("Found metadata for feature type '" + ftName
                        + "', but feature type is not available from any store.");
            }
        }
        return sortedFts;
    }

    private Collection<FeatureType> getAllFeatureTypes() {
        Comparator<FeatureType> comp = new Comparator<FeatureType>() {
            @Override
            public int compare(FeatureType ftMd1, FeatureType ftMd2) {
                QName a = ftMd1.getName();
                QName b = ftMd2.getName();
                int order = a.getNamespaceURI().compareTo(b.getNamespaceURI());
                if (order == 0) {
                    order = a.getLocalPart().compareTo(b.getLocalPart());
                }
                return order;
            }
        };
        Collection<FeatureType> sortedFts = new TreeSet<FeatureType>(comp);
        for (FeatureType ft : service.getFeatureTypes()) {
            FeatureStore fs = service.getStore(ft.getName());
            if (fs.isMapped(ft.getName())) {
                sortedFts.add(ft);
            }
        }
        return sortedFts;
    }

    /**
     * Returns an <code>XMLStreamWriter</code> for writing an XML response document.
     * 
     * @param writer
     *            writer to write the XML to, must not be <code>null</code>
     * @param mimeType
     *            mime type, must not be <code>null</code>
     * @param schemaLocation
     *            value for the 'xsi:schemaLocation' attribute in the root element, can be <code>null</code>
     * @return XML stream writer object that takes care of putting the schemaLocation in the root element
     * @throws XMLStreamException
     * @throws IOException
     */
    public static XMLStreamWriter getXMLResponseWriter(HttpResponseBuffer writer, String mimeType,
            String schemaLocation) throws XMLStreamException, IOException {

        boolean needsEncoding = mimeType.startsWith("text");
        XMLStreamWriter xmlWriter = writer.getXMLWriter(needsEncoding);
        // call setContentType(...) after setCharacterEncoding(...) to avoid problems on certain web containers
        // see http://tracker.deegree.org/deegree-services/ticket/323
        writer.setContentType(mimeType);
        if (schemaLocation == null) {
            return xmlWriter;
        }
        return new SchemaLocationXMLStreamWriter(xmlWriter, schemaLocation);
    }

    private void sendServiceException(Version requestVersion, OWSException e, HttpResponseBuffer response)
            throws ServletException {
        XMLExceptionSerializer serializer = getExceptionSerializer(requestVersion);
        sendException(null, serializer, e, response);
    }

    private void sendSoapException(SOAPEnvelope soapDoc, SOAPFactory factory, HttpResponseBuffer response,
            OWSException e, ServletRequest request, Version requestVersion) throws OMException, ServletException {
        XMLExceptionSerializer serializer = getExceptionSerializer(requestVersion);
        sendSOAPException(soapDoc.getHeader(), factory, response, e, serializer, null, null,
                request.getServerName(), request.getCharacterEncoding());
    }

    @Override
    public XMLExceptionSerializer getExceptionSerializer(Version requestVersion) {
        XMLExceptionSerializer serializer = getDefaultExceptionSerializer();
        if (VERSION_100.equals(requestVersion)) {
            serializer = new PreOWSExceptionReportSerializer("application/vnd.ogc.se_xml");
        } else if (VERSION_110.equals(requestVersion)) {
            serializer = new OWS100ExceptionReportSerializer();
        } else if (VERSION_200.equals(requestVersion)) {
            serializer = new OWS110ExceptionReportSerializer(VERSION_200);
        }
        return serializer;
    }

    private XMLExceptionSerializer getDefaultExceptionSerializer() {
        List<String> offeredVersions = getOfferedVersions();
        if (offeredVersions.contains(VERSION_200.toString())) {
            return new OWS110ExceptionReportSerializer(VERSION_200);
        } else if (offeredVersions.contains(VERSION_110.toString())) {
            return new OWS100ExceptionReportSerializer();
        }
        return new PreOWSExceptionReportSerializer("application/vnd.ogc.se_xml");
    }

    /**
     * Determines the requested output/input format.
     * 
     * @param requestVersion
     *            version of the WFS request, must not be <code>null</code>
     * @param format
     *            mimeType or identifier for the format, can be <code>null</code>
     * @param locator
     * @return format handler to use, never <code>null</code>
     * @throws OWSException
     */
    private Format determineFormat(Version requestVersion, String format, String locator) throws OWSException {

        Format outputFormat = null;

        if (format == null) {
            // default values for the different WFS version
            if (VERSION_100.equals(requestVersion)) {
                outputFormat = gmlVersionToFormat.get(GMLVersion.GML_2);
            } else if (VERSION_110.equals(requestVersion)) {
                outputFormat = gmlVersionToFormat.get(GMLVersion.GML_31);
            } else if (VERSION_200.equals(requestVersion)) {
                outputFormat = gmlVersionToFormat.get(GMLVersion.GML_32);
            }
        } else {
            if ("GML2".equals(format) || "XMLSCHEMA".equals(format)) {
                outputFormat = gmlVersionToFormat.get(GMLVersion.GML_2);
            } else if ("GML3".equals(format)) {
                outputFormat = gmlVersionToFormat.get(GMLVersion.GML_31);
            } else {
                outputFormat = mimeTypeToFormat.get(format);
            }
        }
        if (outputFormat == null) {
            String msg = "This WFS is not configured to handle the output/input format '" + format + "'";
            throw new OWSException(msg, INVALID_PARAMETER_VALUE, locator);
        }
        return outputFormat;
    }

    Collection<String> getOutputFormats() {
        return mimeTypeToFormat.keySet();
    }

    public int getQueryMaxFeatures() {
        // TODO Auto-generated method stub
        return queryMaxFeatures;
    }

    public boolean getCheckAreaOfUse() {
        // TODO Auto-generated method stub
        return checkAreaOfUse;
    }

    public ICRS getDefaultQueryCrs() {
        return defaultQueryCRS;
    }

    /**
     * Checks if a request version can be handled by this controller (i.e. if is supported by the implementation *and*
     * offered by the current configuration).
     * <p>
     * NOTE: This method does use exception code {@link OWSException#INVALID_PARAMETER_VALUE}, not
     * {@link OWSException#VERSION_NEGOTIATION_FAILED} -- the latter should only be used for failed GetCapabilities
     * requests.
     * </p>
     * 
     * @param requestedVersion
     *            version to be checked, may be null (causes exception)
     * @return <code>requestedVersion</code> (if it is not null), or highest version supported
     * @throws OWSException
     *             if the requested version is not available
     */
    @Override
    protected Version checkVersion(Version requestedVersion) throws OWSException {

        Version version = requestedVersion;
        if (requestedVersion == null) {
            LOG.debug("Assuming version 1.1.0 (the only one that has an optional version attribute).");
            version = VERSION_110;
        }
        if (!offeredVersions.contains(version)) {
            throw new OWSException(
                    Messages.get("CONTROLLER_UNSUPPORTED_VERSION", version, getOfferedVersionsString()),
                    OWSException.INVALID_PARAMETER_VALUE);
        }
        return version;
    }

}