/*
* Constellation - An open source and standard compliant SDI
* http://www.constellation-sdi.org
*
* Copyright 2014 Geomatys.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.constellation.wfs.ws;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import org.apache.sis.referencing.IdentifiedObjects;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.util.Utilities;
import org.apache.sis.util.logging.Logging;
import org.apache.sis.xml.MarshallerPool;
import org.constellation.ServiceDef;
import org.constellation.ServiceDef.Version;
import org.constellation.configuration.ConfigurationException;
import org.constellation.configuration.FormatURL;
import org.constellation.configuration.Layer;
import org.constellation.dto.Details;
import org.constellation.provider.Data;
import org.constellation.provider.FeatureData;
import org.constellation.security.SecurityManagerHolder;
import org.constellation.util.QNameComparator;
import org.constellation.util.QnameLocalComparator;
import org.constellation.wfs.ws.rs.FeatureCollectionWrapper;
import org.constellation.wfs.ws.rs.ValueCollectionWrapper;
import org.constellation.ws.CstlServiceException;
import org.constellation.ws.LayerWorker;
import org.constellation.ws.UnauthorizedException;
import org.geotoolkit.data.FeatureCollection;
import org.geotoolkit.data.FeatureStore;
import org.geotoolkit.data.FeatureStoreUtilities;
import org.geotoolkit.data.memory.GenericReprojectFeatureIterator;
import org.geotoolkit.data.query.QueryBuilder;
import org.geotoolkit.factory.Hints;
import org.geotoolkit.factory.HintsPending;
import org.geotoolkit.feature.Feature;
import org.geotoolkit.feature.FeatureTypeUtilities;
import org.geotoolkit.feature.type.FeatureType;
import org.geotoolkit.feature.type.GeometryType;
import org.geotoolkit.feature.type.PropertyDescriptor;
import org.geotoolkit.feature.type.PropertyType;
import org.geotoolkit.feature.xml.Utils;
import org.geotoolkit.feature.xml.jaxb.JAXBFeatureTypeWriter;
import org.geotoolkit.feature.xml.jaxp.JAXPStreamFeatureReader;
import org.geotoolkit.filter.binding.Binding;
import org.geotoolkit.filter.binding.Bindings;
import org.geotoolkit.filter.visitor.FillCrsVisitor;
import org.geotoolkit.filter.visitor.IsValidSpatialFilterVisitor;
import org.geotoolkit.filter.visitor.ListingPropertyVisitor;
import org.geotoolkit.geometry.jts.JTS;
import org.geotoolkit.gml.GeometrytoJTS;
import org.geotoolkit.gml.xml.AbstractGML;
import org.geotoolkit.gml.xml.DirectPosition;
import org.geotoolkit.gml.xml.v311.AbstractGeometryType;
import org.geotoolkit.gml.xml.v311.FeaturePropertyType;
import org.geotoolkit.ogc.xml.XMLFilter;
import org.geotoolkit.ogc.xml.XMLLiteral;
import org.geotoolkit.ogc.xml.v200.BBOXType;
import org.geotoolkit.ows.xml.AbstractCapabilitiesBase;
import org.geotoolkit.ows.xml.AbstractDomain;
import org.geotoolkit.ows.xml.AbstractOperationsMetadata;
import org.geotoolkit.ows.xml.AbstractServiceIdentification;
import org.geotoolkit.ows.xml.AbstractServiceProvider;
import org.geotoolkit.ows.xml.AcceptVersions;
import org.geotoolkit.ows.xml.RequestBase;
import org.geotoolkit.ows.xml.Sections;
import org.geotoolkit.referencing.CRS;
import org.geotoolkit.sld.xml.StyleXmlIO;
import org.geotoolkit.wfs.xml.CreateStoredQuery;
import org.geotoolkit.wfs.xml.CreateStoredQueryResponse;
import org.geotoolkit.wfs.xml.DeleteElement;
import org.geotoolkit.wfs.xml.DescribeFeatureType;
import org.geotoolkit.wfs.xml.DescribeStoredQueries;
import org.geotoolkit.wfs.xml.DescribeStoredQueriesResponse;
import org.geotoolkit.wfs.xml.DropStoredQuery;
import org.geotoolkit.wfs.xml.DropStoredQueryResponse;
import org.geotoolkit.wfs.xml.FeatureRequest;
import org.geotoolkit.wfs.xml.FeatureTypeList;
import org.geotoolkit.wfs.xml.GetCapabilities;
import org.geotoolkit.wfs.xml.GetFeature;
import org.geotoolkit.wfs.xml.GetGmlObject;
import org.geotoolkit.wfs.xml.GetPropertyValue;
import org.geotoolkit.wfs.xml.IdentifierGenerationOptionType;
import org.geotoolkit.wfs.xml.InsertElement;
import org.geotoolkit.wfs.xml.ListStoredQueries;
import org.geotoolkit.wfs.xml.ListStoredQueriesResponse;
import org.geotoolkit.wfs.xml.LockFeature;
import org.geotoolkit.wfs.xml.LockFeatureResponse;
import org.geotoolkit.wfs.xml.Parameter;
import org.geotoolkit.wfs.xml.ParameterExpression;
import org.geotoolkit.wfs.xml.Property;
import org.geotoolkit.wfs.xml.Query;
import org.geotoolkit.wfs.xml.QueryExpressionText;
import org.geotoolkit.wfs.xml.ReplaceElement;
import org.geotoolkit.wfs.xml.ResultTypeType;
import org.geotoolkit.wfs.xml.StoredQueries;
import org.geotoolkit.wfs.xml.StoredQuery;
import org.geotoolkit.wfs.xml.StoredQueryDescription;
import org.geotoolkit.wfs.xml.Transaction;
import org.geotoolkit.wfs.xml.TransactionResponse;
import org.geotoolkit.wfs.xml.UpdateElement;
import org.geotoolkit.wfs.xml.WFSCapabilities;
import org.geotoolkit.wfs.xml.WFSMarshallerPool;
import org.geotoolkit.wfs.xml.WFSXmlFactory;
import org.geotoolkit.wfs.xml.v110.FeatureCollectionType;
import org.geotoolkit.wfs.xml.v200.ObjectFactory;
import org.geotoolkit.wfs.xml.v200.PropertyName;
import org.geotoolkit.wfs.xml.v200.QueryExpressionTextType;
import org.geotoolkit.wfs.xml.v200.QueryType;
import org.geotoolkit.wfs.xml.v200.StoredQueryDescriptionType;
import org.geotoolkit.xsd.xml.v2001.Schema;
import org.opengis.filter.BinaryComparisonOperator;
import org.opengis.filter.BinaryLogicOperator;
import org.opengis.filter.Filter;
import org.opengis.filter.capability.FilterCapabilities;
import org.opengis.filter.identity.FeatureId;
import org.opengis.filter.sort.SortBy;
import org.opengis.filter.spatial.BinarySpatialOperator;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.NoSuchAuthorityCodeException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.TransformException;
import org.opengis.util.CodeList;
import org.opengis.util.FactoryException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.inject.Named;
import javax.xml.bind.JAXBElement;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Level;
import org.apache.sis.xml.Namespaces;
import org.constellation.wfs.ws.WFSConstants.GetXSD;
import static org.constellation.wfs.ws.WFSConstants.IDENTIFIER_FILTER;
import static org.constellation.wfs.ws.WFSConstants.IDENTIFIER_PARAM;
import static org.constellation.wfs.ws.WFSConstants.OPERATIONS_METADATA_V110;
import static org.constellation.wfs.ws.WFSConstants.OPERATIONS_METADATA_V200;
import static org.constellation.wfs.ws.WFSConstants.TYPE_PARAM;
import static org.constellation.wfs.ws.WFSConstants.UNKNOW_TYPENAME;
import org.geotoolkit.data.FeatureStoreRuntimeException;
import org.geotoolkit.data.FeatureWriter;
import org.geotoolkit.data.memory.ExtendedFeatureStore;
import org.geotoolkit.data.om.NetCDFFeatureStore;
import org.geotoolkit.data.session.Session;
import org.geotoolkit.feature.type.ComplexType;
import org.geotoolkit.util.NamesExt;
import org.geotoolkit.feature.xml.XSDFeatureStore;
import static org.geotoolkit.ows.xml.OWSExceptionCode.INVALID_PARAMETER_VALUE;
import static org.geotoolkit.ows.xml.OWSExceptionCode.INVALID_VALUE;
import static org.geotoolkit.ows.xml.OWSExceptionCode.MISSING_PARAMETER_VALUE;
import static org.geotoolkit.ows.xml.OWSExceptionCode.NO_APPLICABLE_CODE;
import static org.geotoolkit.ows.xml.OWSExceptionCode.OPERATION_NOT_SUPPORTED;
import static org.geotoolkit.ows.xml.OWSExceptionCode.VERSION_NEGOTIATION_FAILED;
import static org.geotoolkit.wfs.xml.WFSXmlFactory.buildBBOX;
import static org.geotoolkit.wfs.xml.WFSXmlFactory.buildCreateStoredQueryResponse;
import static org.geotoolkit.wfs.xml.WFSXmlFactory.buildDescribeStoredQueriesResponse;
import static org.geotoolkit.wfs.xml.WFSXmlFactory.buildDropStoredQueryResponse;
import static org.geotoolkit.wfs.xml.WFSXmlFactory.buildFeatureCollection;
import static org.geotoolkit.wfs.xml.WFSXmlFactory.buildFeatureType;
import static org.geotoolkit.wfs.xml.WFSXmlFactory.buildFeatureTypeList;
import static org.geotoolkit.wfs.xml.WFSXmlFactory.buildListStoredQueriesResponse;
import static org.geotoolkit.wfs.xml.WFSXmlFactory.buildSections;
import static org.geotoolkit.wfs.xml.WFSXmlFactory.buildTransactionResponse;
import static org.geotoolkit.wfs.xml.WFSXmlFactory.buildValueCollection;
import static org.geotoolkit.wfs.xml.WFSXmlFactory.buildWFSCapabilities;
import org.geotoolkit.xsd.xml.v2001.FormChoice;
import org.geotoolkit.xsd.xml.v2001.Import;
import org.opengis.util.GenericName;
/**
*
* @author Guilhem Legal (Geomatys)
*/
@Named
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class DefaultWFSWorker extends LayerWorker implements WFSWorker {
/**
* Base known CRS.
*/
private final static List<String> DEFAULT_CRS = new ArrayList<>();
static {
DEFAULT_CRS.add("urn:ogc:def:crs:EPSG:7.01:4326");
DEFAULT_CRS.add("urn:ogc:def:crs:EPSG:7.01:3395");
}
private List<StoredQueryDescription> storedQueries = new ArrayList<>();
private final boolean isTransactionnal;
public DefaultWFSWorker(final String id) {
super(id, ServiceDef.Specification.WFS);
if (isStarted) {
LOGGER.log(Level.INFO, "WFS worker {0} running", id);
}
final String isTransactionnalProp = getProperty("transactionnal");
if (isTransactionnalProp != null) {
isTransactionnal = Boolean.parseBoolean(isTransactionnalProp);
} else {
boolean t = false;
try {
final Details details = serviceBusiness.getInstanceDetails("wfs", id, null);
t = details.isTransactional();
} catch (ConfigurationException ex) {
LOGGER.log(Level.WARNING, null, ex);
}
isTransactionnal = t;
}
// loading stored queries
loadStoredQueries();
}
private void loadStoredQueries() {
try {
final Object obj = serviceBusiness.getExtraConfiguration("WFS", getId(), "StoredQueries.xml", getMarshallerPool());
if (obj instanceof StoredQueries) {
StoredQueries candidate = (StoredQueries) obj;
this.storedQueries = candidate.getStoredQuery();
} else {
LOGGER.log(Level.WARNING, "The storedQueries File does not contains proper object");
}
} catch (ConfigurationException ex) {
LOGGER.log(Level.WARNING, "ConfigurationException while unmarshalling the stored queries File", ex);
}
// we verify if the identifier query is loaded (if not we load it)
boolean foundID = false;
for (StoredQueryDescription squery : storedQueries) {
if ("urn:ogc:def:query:OGC-WFS::GetFeatureById".equals(squery.getId())) {
foundID = true;
break;
}
}
if (!foundID) {
final List<QName> typeNames = getConfigurationLayerNames(null);
Collections.sort(typeNames, new QNameComparator());
final QueryType query = new QueryType(IDENTIFIER_FILTER, typeNames, "2.0.0");
final QueryExpressionTextType queryEx = new QueryExpressionTextType("urn:ogc:def:queryLanguage:OGC-WFS::WFS_QueryExpression", null, typeNames);
final ObjectFactory factory = new ObjectFactory();
queryEx.getContent().add(factory.createQuery(query));
final StoredQueryDescriptionType idQ = new StoredQueryDescriptionType("urn:ogc:def:query:OGC-WFS::GetFeatureById", "Identifier query" , "filter on feature identifier", IDENTIFIER_PARAM, queryEx);
storedQueries.add(idQ);
}
// we verify if the type query is loaded (if not we load it)
boolean foundT = false;
for (StoredQueryDescription squery : storedQueries) {
if ("urn:ogc:def:storedQuery:OGC-WFS::GetFeatureByType".equals(squery.getId())) {
foundT = true;
break;
}
}
if (!foundT) {
final List<QName> typeNames = new ArrayList<>();
//typeNames.add(new QName("$typeName")); not XML valid (CITE TEST)
final QueryType query = new QueryType(null, typeNames, "2.0.0");
final QueryExpressionTextType queryEx = new QueryExpressionTextType("urn:ogc:def:queryLanguage:OGC-WFS::WFS_QueryExpression", null, typeNames);
final ObjectFactory factory = new ObjectFactory();
queryEx.getContent().add(factory.createQuery(query));
final StoredQueryDescriptionType idQ = new StoredQueryDescriptionType("urn:ogc:def:storedQuery:OGC-WFS::GetFeatureByType", "By type query" , "filter on feature type", TYPE_PARAM, queryEx);
storedQueries.add(idQ);
}
}
private void storedQueries() {
serviceBusiness.setExtraConfiguration("WFS", getId(), "StoredQueries.xml", new StoredQueries(storedQueries), getMarshallerPool());
}
@Override
protected MarshallerPool getMarshallerPool() {
return WFSMarshallerPool.getInstance();
}
/**
* {@inheritDoc }
*/
@Override
public WFSCapabilities getCapabilities(final GetCapabilities request) throws CstlServiceException {
LOGGER.log(logLevel, "GetCapabilities request proccesing");
final long start = System.currentTimeMillis();
final String userLogin = getUserLogin();
//choose the best version from acceptVersion
final AcceptVersions versions = request.getAcceptVersions();
if (versions != null) {
Version max = null;
for (String v : versions.getVersion()) {
final Version vv = new Version(v);
if (isSupportedVersion(v)) {
if (max == null || vv.compareTo(max) > 1) {
max = vv;
}
}
}
if (max != null) {
request.setVersion(max.toString());
}
}
// we verify the base attribute
verifyBaseRequest(request, false, true);
final String currentVersion = request.getVersion().toString();
//set the current updateSequence parameter
final boolean returnUS = returnUpdateSequenceDocument(request.getUpdateSequence());
if (returnUS) {
return buildWFSCapabilities(currentVersion, getCurrentUpdateSequence());
}
Sections sections = request.getSections();
if (sections == null) {
sections = buildSections(currentVersion, Arrays.asList("All"));
}
final AbstractCapabilitiesBase cachedCapabilities = (WFSCapabilities) getCapabilitiesFromCache(currentVersion, null);
if (cachedCapabilities != null) {
return (WFSCapabilities) cachedCapabilities.applySections(sections);
}
final Details skeleton = getStaticCapabilitiesObject("WFS", null);
final WFSCapabilities inCapabilities = WFSConstants.createCapabilities(currentVersion, skeleton);
final FeatureTypeList ftl = buildFeatureTypeList(currentVersion);
/*
* layer providers
*/
final List<QName> layerNames = getConfigurationLayerNames(userLogin);
Collections.sort(layerNames, new QnameLocalComparator());
for (final QName layerName : layerNames) {
final Data layer = getLayerReference(userLogin, layerName);
final Layer configLayer = getConfigurationLayer(layerName, userLogin);
if (layer instanceof FeatureData) {
final FeatureData fld = (FeatureData) layer;
final FeatureType type;
try {
type = getFeatureTypeFromLayer(fld);
} catch (DataStoreException ex) {
LOGGER.log(Level.WARNING, "Error while getting featureType for:{0}\ncause:{1}", new Object[]{fld.getName(), ex.getMessage()});
continue;
}
final org.geotoolkit.wfs.xml.FeatureType ftt;
try {
final String defaultCRS = getCRSCode(type);
final String title;
if (configLayer.getTitle() != null) {
title = configLayer.getTitle();
} else {
title = fld.getName().tip().toString();
}
ftt = buildFeatureType(
currentVersion,
layerName,
title,
defaultCRS,
DEFAULT_CRS,
toBBox(fld.getStore(), fld.getName(), currentVersion));
/*
* we apply the layer customization
*/
ftt.setAbstract(configLayer.getAbstrac());
if (!configLayer.getKeywords().isEmpty()) {
ftt.addKeywords(configLayer.getKeywords());
}
FormatURL metadataURL = configLayer.getMetadataURL();
if (metadataURL != null) {
ftt.addMetadataURL(metadataURL.getOnlineResource().getValue(),
metadataURL.getType(),
metadataURL.getFormat());
}
if (!configLayer.getCrs().isEmpty()) {
ftt.setOtherCRS(configLayer.getCrs());
}
// we add the feature type description to the list
ftl.addFeatureType(ftt);
} catch (FactoryException ex) {
Logging.unexpectedException(LOGGER, ex);
}
} else {
LOGGER.log(Level.WARNING, "The layer:{0} is not a feature layer", layerName);
}
}
final AbstractOperationsMetadata om;
if (currentVersion.equals("2.0.0")) {
om = OPERATIONS_METADATA_V200.clone();
} else {
om = OPERATIONS_METADATA_V110.clone();
}
om.updateURL(getServiceUrl());
if (!isTransactionnal) {
om.removeOperation("Transaction");
final AbstractDomain cst = om.getConstraint("ImplementsTransactionalWFS");
if (cst != null) {
cst.setDefaultValue("FALSE");
}
}
final AbstractServiceProvider sp = inCapabilities.getServiceProvider();
final AbstractServiceIdentification si = inCapabilities.getServiceIdentification();
final FilterCapabilities fc;
if (currentVersion.equals("2.0.0")) {
fc = WFSConstants.FILTER_CAPABILITIES_V200;
} else {
fc = WFSConstants.FILTER_CAPABILITIES_V110;
}
final WFSCapabilities result = buildWFSCapabilities(currentVersion, si, sp, om, ftl, fc);
putCapabilitiesInCache(currentVersion, null, result);
LOGGER.log(logLevel, "GetCapabilities treated in {0}ms", (System.currentTimeMillis() - start));
return (WFSCapabilities) result.applySections(sections);
}
private String getCRSCode(FeatureType type) throws FactoryException{
final String defaultCRS;
if (type.getGeometryDescriptor() != null && type.getGeometryDescriptor().getCoordinateReferenceSystem() != null) {
final CoordinateReferenceSystem crs = type.getGeometryDescriptor().getCoordinateReferenceSystem();
//todo wait for martin fix
String id = org.geotoolkit.referencing.IdentifiedObjects.lookupIdentifier(crs, true);
if (id == null) {
id = IdentifiedObjects.getIdentifierOrName(crs);
}
if (id != null) {
defaultCRS = "urn:ogc:def:crs:" + id.replaceAll(":", ":7.01:");
// final String defaultCRS = IdentifiedObjects.lookupIdentifier(Citations.URN_OGC,
// type.getGeometryDescriptor().getCoordinateReferenceSystem(), true);
} else {
defaultCRS = "urn:ogc:def:crs:EPSG:7.01:4326";
}
} else {
defaultCRS = "urn:ogc:def:crs:EPSG:7.01:4326";
}
return defaultCRS;
}
/**
* {@inheritDoc }
*/
@Override
public Object describeFeatureType(final DescribeFeatureType request) throws CstlServiceException {
LOGGER.log(logLevel, "DecribeFeatureType request proccesing");
final long start = System.currentTimeMillis();
// we verify the base attribute
verifyBaseRequest(request, false, false);
final String currentVersion = request.getVersion().toString();
final String userLogin = getUserLogin();
final String gmlVersion;
if ("2.0.0".equals(currentVersion)) {
gmlVersion = "3.2.1";
} else {
gmlVersion = "3.1.1";
}
final JAXBFeatureTypeWriter writer = new JAXBFeatureTypeWriter(gmlVersion);
final List<QName> names = request.getTypeName();
final List<FeatureType> types = new ArrayList<>();
final Map<String, String> locations = new HashMap<>();
//XSDFeatureStore may provide the xsd themselves
final Map<FeatureType,Schema> declaredSchema = new HashMap<>();
if (names.isEmpty()) {
//search all types
for (final QName name : getConfigurationLayerNames(userLogin)) {
final Data layer = getLayerReference(userLogin, name);
if (!(layer instanceof FeatureData)) {continue;}
try {
FeatureData fLayer = (FeatureData)layer;
FeatureStore store = fLayer.getStore();
if (store instanceof ExtendedFeatureStore) store = ((ExtendedFeatureStore)store).getWrapped();
if (store instanceof XSDFeatureStore) {
final Map params = (Map)((XSDFeatureStore)store).getSchema(layer.getName());
if(params.size()==1 && params.get(params.keySet().iterator().next()) instanceof Schema){
final FeatureType ft = getFeatureTypeFromLayer(fLayer);
declaredSchema.put(ft, (Schema) params.get(params.keySet().iterator().next()));
types.add(ft);
}else{
locations.putAll(params);
}
} else {
types.add(getFeatureTypeFromLayer(fLayer));
}
} catch (DataStoreException ex) {
LOGGER.log(Level.WARNING, "error while getting featureType for:{0}", layer.getName());
}
}
} else {
//search only the given list
for (final QName name : names) {
if (name == null) {continue;}
final GenericName n = Utils.getNameFromQname(name);
if (layersContainsKey(userLogin, n) == null) {
throw new CstlServiceException(UNKNOW_TYPENAME + name, INVALID_PARAMETER_VALUE, "typenames");
}
final Data layer = getLayerReference(userLogin, n);
if(!(layer instanceof FeatureData)) {
throw new CstlServiceException(UNKNOW_TYPENAME + name, INVALID_PARAMETER_VALUE, "typenames");
}
try {
FeatureData fLayer = (FeatureData)layer;
ExtendedFeatureStore store = (ExtendedFeatureStore) fLayer.getStore();
if (store.getWrapped() instanceof XSDFeatureStore) {
final Map params = (Map)((XSDFeatureStore)store.getWrapped()).getSchema(layer.getName());
if(params.size()==1 && params.get(params.keySet().iterator().next()) instanceof Schema){
final FeatureType ft = getFeatureTypeFromLayer(fLayer);
declaredSchema.put(ft, (Schema) params.get(params.keySet().iterator().next()));
types.add(ft);
}else{
locations.putAll(params);
}
} else {
types.add(getFeatureTypeFromLayer(fLayer));
}
} catch (DataStoreException ex) {
LOGGER.log(Level.WARNING, "error while getting featureType for:{0}", layer.getName());
}
}
}
final int size = types.size();
for (int i = 0; i < size; i++) {
try {
types.set(i, FeatureTypeUtilities.excludePrimaryKeyFields(types.get(i)));
} catch (IllegalArgumentException ex) {
LOGGER.log(Level.SEVERE, "error while excluding primary keys", ex);
}
}
if (request.getOutputFormat().equals("application/schema+json")) {
return new org.constellation.wfs.ws.rs.FeatureTypeList(types);
}
/*
* Most simple case. we have only one feature type
*/
final Schema schema;
if (size == 1) {
final FeatureType type = types.get(0);
final String tn = type.getName().tip().toString();
final String tnmsp = NamesExt.getNamespace(type.getName());
if(declaredSchema.containsKey(type)){
schema = declaredSchema.get(type);
}else{
schema = writer.getSchemaFromFeatureType(type);
final Set<String> nmsps = Utils.listAllNamespaces(type);
nmsps.remove(tnmsp);
nmsps.remove(Namespaces.GML);
nmsps.remove("http://www.opengis.net/gml");
for (String nmsp : nmsps) {
schema.addImport(new Import(nmsp, getServiceUrl() + "request=xsd&version=" + currentVersion + "&targetNamespace=" + nmsp + "&typename=ns:" + tn + "&namespace=xmlns(ns=" + tnmsp + ")"));
}
}
/*
* Second case. we have many feature type in the same namespace
*/
} else if (AllInSameNamespace(types)) {
schema = writer.getSchemaFromFeatureType(types);
final Set<String> nmsps = new HashSet<>();
for (FeatureType type : types) {
nmsps.addAll(Utils.listAllNamespaces(type));
}
nmsps.remove(NamesExt.getNamespace(types.get(0).getName()));
nmsps.remove(Namespaces.GML);
nmsps.remove("http://www.opengis.net/gml");
for (String nmsp : nmsps) {
schema.addImport(new Import(nmsp, getServiceUrl() + "request=xsd&version=" + currentVersion + "&targetNamespace=" + nmsp));
}
/*
* third case. we have many feature type in the many namespace.
* send an xsd pointing on various describeFeatureRequest
*/
} else {
Map<String, List<FeatureType>> typeMap = splitByNamespace(types);
if (typeMap.containsKey(null)) {
final List<FeatureType> fts = typeMap.get(null);
schema = writer.getSchemaFromFeatureType(fts);
typeMap.remove(null);
} else {
schema = new Schema(FormChoice.QUALIFIED, null);
}
for (String nmsp : typeMap.keySet()) {
final List<FeatureType> fts = typeMap.get(nmsp);
StringBuilder sb = new StringBuilder();
for (FeatureType ft : fts) {
sb.append("ns:").append(ft.getName().tip().toString()).append(',');
}
sb.delete(sb.length() -1, sb.length());
final String schemaLocation = getServiceUrl() + "request=DescribeFeatureType&service=WFS&version=" + currentVersion + "&typename=" + sb.toString() + "&namespace=xmlns(ns=" + nmsp + ")";
schema.addImport(new Import(nmsp, schemaLocation));
}
}
for (Entry<String, String> location : locations.entrySet()) {
schema.addImport(new Import(location.getKey(), location.getValue()));
}
LOGGER.log(logLevel, "DescribeFeatureType treated in {0}ms", (System.currentTimeMillis() - start));
return schema;
}
private boolean AllInSameNamespace(final List<FeatureType> types) {
if (!types.isEmpty() || types.size() == 1) {
String firstNmsp = NamesExt.getNamespace(types.get(0).getName());
for (int i = 1; i < types.size(); i++) {
FeatureType type = types.get(i);
if (!firstNmsp.equals(NamesExt.getNamespace(type.getName()))) {
return false;
}
}
} else if (types.isEmpty()) {
return false;
}
return true;
}
private Map<String, List<FeatureType>> splitByNamespace(final List<FeatureType> types) {
Map<String, List<FeatureType>> results = new HashMap<>();
for (FeatureType type : types) {
final String nmsp = NamesExt.getNamespace(type.getName());
if (results.containsKey(nmsp)) {
results.get(nmsp).add(type);
} else {
final List<FeatureType> ft = new ArrayList<>();
ft.add(type);
results.put(nmsp, ft);
}
}
return results;
}
@Override
public Schema getXsd(final GetXSD request) throws CstlServiceException {
final String userLogin = getUserLogin();
final String gmlVersion;
if ("2.0.0".equals(request.version)) {
gmlVersion = "3.2.1";
} else {
gmlVersion = "3.1.1";
}
final JAXBFeatureTypeWriter writer = new JAXBFeatureTypeWriter(gmlVersion);
final List<FeatureType> types = new ArrayList<>();
final String suffix;
if (request.featureType == null) {
//search all types
for (final QName name : getConfigurationLayerNames(userLogin)) {
final Data layer = getLayerReference(userLogin, name);
if (!(layer instanceof FeatureData)) {continue;}
try {
types.add(getFeatureTypeFromLayer((FeatureData)layer));
} catch (DataStoreException ex) {
LOGGER.log(Level.WARNING, "error while getting featureType for:{0}", layer.getName());
}
}
suffix = "";
} else {
final GenericName n = Utils.getNameFromQname(request.featureType);
if (layersContainsKey(userLogin, n) == null) {
throw new CstlServiceException(UNKNOW_TYPENAME + request.featureType, INVALID_PARAMETER_VALUE, "typenames");
}
final Data layer = getLayerReference(userLogin, n);
if(!(layer instanceof FeatureData)) {
throw new CstlServiceException(UNKNOW_TYPENAME + request.featureType, INVALID_PARAMETER_VALUE, "typenames");
}
try {
types.add(getFeatureTypeFromLayer((FeatureData)layer));
} catch (DataStoreException ex) {
LOGGER.log(Level.WARNING, "error while getting featureType for:"+ layer.getName(), ex);
}
suffix = "&typename=ns:" + request.featureType.getLocalPart() + "&namespace=xmlns(ns=" + request.featureType.getNamespaceURI() + ")";
}
Schema schema = writer.getExternalSchemaFromFeatureType(request.namespace, types);
final Set<String> nmsps = new HashSet<>();
for (FeatureType type : types) {
nmsps.addAll(Utils.listAllSubNamespaces(type, request.namespace));
}
nmsps.remove(request.namespace);
nmsps.remove(Namespaces.GML);
nmsps.remove("http://www.opengis.net/gml");
for (String nmsp : nmsps) {
schema.addImport(new Import(nmsp, getServiceUrl() + "request=xsd&version=" + request.version + "&targetNamespace=" + nmsp + suffix));
}
return schema;
}
/**
* Extract a FeatureType from a FeatureLayerDetails
*
* @param fld A feature layer object.
* @return A Feature type.
*/
private FeatureType getFeatureTypeFromLayer(final FeatureData fld) throws DataStoreException {
return fld.getStore().getFeatureType(fld.getName());
}
private LinkedHashMap<String,? extends Query> extractStoredQueries(final FeatureRequest request) throws CstlServiceException {
final List<? extends Query> queries = request.getQuery();
final LinkedHashMap<String,Query> result = new LinkedHashMap<>();
for(int i=0,n=queries.size();i<n;i++){
result.put(""+i, queries.get(i));
}
for (StoredQuery storedQuery : request.getStoredQuery()) {
StoredQueryDescription description = null;
final List<? extends Parameter> parameters = storedQuery.getParameter();
for (StoredQueryDescription desc : storedQueries) {
if (desc.getId().equals(storedQuery.getId())) {
description = desc;
break;
}
}
if (description == null) {
throw new CstlServiceException("Unknow stored query: " + storedQuery.getId(), INVALID_PARAMETER_VALUE, "storedQuery");
} else {
for (QueryExpressionText queryEx : description.getQueryExpressionText()) {
for (Object content : queryEx.getContent()) {
if (content instanceof JAXBElement) {
content = ((JAXBElement)content).getValue();
}
if (content instanceof Query) {
final Query query = WFSXmlFactory.cloneQuery((Query)content);
applyParameterOnQuery(query, parameters);
result.put(description.getId(), query);
} else {
throw new CstlServiceException("unexpected query object: " + content, INVALID_PARAMETER_VALUE, "storedQuery");
}
}
}
}
}
return result;
}
private List<String> extractPropertyNames(final List<Object> properties) {
final List<String> requestPropNames = new ArrayList<>();
for (Object obj : properties) {
if (obj instanceof JAXBElement) {
obj = ((JAXBElement) obj).getValue();
}
if (obj instanceof String) {
String pName = (String) obj;
final int pos = pName.lastIndexOf(':');
if (pos != -1) {
pName = pName.substring(pos + 1);
}
requestPropNames.add(pName);
} else if (obj instanceof PropertyName) {
final PropertyName pName = (PropertyName) obj;
if (pName.getValue() != null) {
requestPropNames.add(pName.getValue().getLocalPart());
}
}
}
return requestPropNames;
}
private void putSchemaLocation(final QName typeName, final Map<String, String> schemaLocations, final String version) {
final String namespace = typeName.getNamespaceURI();
if (schemaLocations.containsKey(namespace)) {
LOGGER.severe("TODO multiple typeName schemaLocation");
} else {
String prefix = typeName.getPrefix();
if (prefix == null || prefix.isEmpty()) {
prefix = "ns1";
}
final String url = getServiceUrl();
if (url != null) {
String describeRequest = url + "request=DescribeFeatureType&version=" + version + "&service=WFS";
describeRequest = describeRequest + "&namespace=xmlns(" + prefix + "=" + namespace + ")";
final String tnParameter;
if (version.equals("2.0.0")) {
tnParameter = "typenames";
} else {
tnParameter = "typename";
}
describeRequest = describeRequest + "&" + tnParameter + "=" + prefix + ':' + typeName.getLocalPart();
schemaLocations.put(namespace, describeRequest);
}
}
}
private GenericName[] verifyPropertyNames(final QName typeName, final FeatureType ft, final List<String> requestPropNames) throws CstlServiceException {
if (!requestPropNames.isEmpty()) {
final List<GenericName> propertyNames = new ArrayList<>();
for (PropertyDescriptor pdesc : ft.getDescriptors()) {
final GenericName propName = pdesc.getName();
if (pdesc.getMinOccurs() > 0) {
if (!propertyNames.contains(propName)) {
propertyNames.add(propName);
}
} else if (requestPropNames.contains(propName.tip().toString())) {
propertyNames.add(propName);
}
requestPropNames.remove(propName.tip().toString());
}
// if the requestPropNames is not empty there is unKnown propertyNames
if (!requestPropNames.isEmpty()) {
throw new CstlServiceException("The feature Type " + typeName + " does not have such a property:" + requestPropNames.get(0), INVALID_PARAMETER_VALUE);
}
return propertyNames.toArray(new GenericName[propertyNames.size()]);
} else {
return null;
}
}
/**
* {@inheritDoc }
*/
@Override
public Object getFeature(final GetFeature request) throws CstlServiceException {
LOGGER.log(logLevel, "GetFeature request proccesing");
final long start = System.currentTimeMillis();
// we verify the base attribute
verifyBaseRequest(request, false, false);
long nbMatched = 0;
final String userLogin = getUserLogin();
final String currentVersion = request.getVersion().toString();
final int maxFeatures = request.getCount();
final Integer startIndex = request.getStartIndex();
final List<FeatureCollection> collections = new ArrayList<>();
final Map<String, String> schemaLocations = new HashMap<>();
final Map<String, String> namespaceMapping = request.getPrefixMapping();
if ((request.getQuery() == null || request.getQuery().isEmpty()) && (request.getStoredQuery() == null || request.getStoredQuery().isEmpty())) {
throw new CstlServiceException("You must specify a query!", MISSING_PARAMETER_VALUE);
}
final LinkedHashMap<String, ? extends Query> queries = extractStoredQueries(request);
for (final Query query : queries.values()) {
final List<QName> typeNames;
final Map<String, QName> aliases = new HashMap<>();
if (query.getTypeNames().isEmpty()) {
typeNames = getConfigurationLayerNames(userLogin);
} else {
typeNames = query.getTypeNames();
if (!query.getAliases().isEmpty()) {
for (int i = 0; i < typeNames.size() && i < query.getAliases().size(); i++) {
aliases.put(query.getAliases().get(i), typeNames.get(i));
}
}
}
//decode filter-----------------------------------------------------
final Filter filter = extractJAXBFilter(query.getFilter(), Filter.INCLUDE, namespaceMapping, currentVersion);
//decode crs--------------------------------------------------------
final CoordinateReferenceSystem queryCRS = extractCRS(query.getSrsName());
//decode property names---------------------------------------------
final List<String> requestPropNames = extractPropertyNames(query.getPropertyNames());
//decode sort by----------------------------------------------------
final List<SortBy> sortBys = visitJaxbSortBy(query.getSortBy(), namespaceMapping, currentVersion);
final QueryBuilder queryBuilder = new QueryBuilder();
queryBuilder.setCRS(queryCRS);
if (!sortBys.isEmpty()) {
queryBuilder.setSortBy(sortBys.toArray(new SortBy[sortBys.size()]));
}
if (startIndex != 0){
queryBuilder.setStartIndex(startIndex);
}
for (QName typeName : typeNames) {
final GenericName fullTypeName = Utils.getNameFromQname(typeName);
if (layersContainsKey(userLogin, fullTypeName) == null) {
throw new CstlServiceException(UNKNOW_TYPENAME + typeName, INVALID_PARAMETER_VALUE, "typenames");
}
final Data layerD = getLayerReference(userLogin, fullTypeName);
if (!(layerD instanceof FeatureData)) {continue;}
final FeatureData layer = (FeatureData) layerD;
final FeatureType ft;
try {
ft = getFeatureTypeFromLayer(layer);
} catch (DataStoreException ex) {
throw new CstlServiceException(ex);
}
// suplied typeName can be imcomplete (no namespace)
final QName realName = Utils.getQnameFromName(ft.getName());
// we ensure that the property names are contained in the feature type and add the mandatory attribute to the list
queryBuilder.setProperties(verifyPropertyNames(realName, ft, requestPropNames));
queryBuilder.setTypeName(ft.getName());
queryBuilder.setHints(new Hints(HintsPending.FEATURE_HIDE_ID_PROPERTY, Boolean.TRUE));
final Filter cleanFilter = processFilter(ft, filter, aliases);
queryBuilder.setFilter(cleanFilter);
// we verify that all the properties contained in the filter are known by the feature type.
verifyFilterProperty(ft, cleanFilter, aliases);
if (maxFeatures != 0){
queryBuilder.setMaxFeatures(maxFeatures);
}
final Session session = layer.getStore().createSession(false);
final org.geotoolkit.data.query.Query qb = queryBuilder.buildQuery();
FeatureCollection collection = session.getFeatureCollection(qb);
int colSize = 0;
// look for matching count
queryBuilder.setMaxFeatures(null);
try {
colSize = collection.size();
nbMatched = nbMatched + colSize;
} catch (FeatureStoreRuntimeException ex) {
throw new CstlServiceException(ex);
}
if (colSize>0) {
if(queryCRS == null){
try {
//ensure axes are in the declared order, since we use urn epsg, we must comply
//to proper epsg axis order
final String defaultCRS = getCRSCode(ft);
final CoordinateReferenceSystem rcrs = CRS.decode(defaultCRS);
if(!CRS.equalsIgnoreMetadata(rcrs, ft.getCoordinateReferenceSystem())){
collection = GenericReprojectFeatureIterator.wrap(collection, CRS.decode(defaultCRS));
}
} catch (FactoryException ex) {
LOGGER.log(Level.WARNING, ex.getMessage(), ex);
}
}
collections.add(collection);
// we write The SchemaLocation
putSchemaLocation(realName, schemaLocations, currentVersion);
}
}
}
final String gmlVersion;
if ("text/xml; subtype=gml/3.1.1".equals(request.getOutputFormat()) ||
"text/gml; subtype=gml/3.1.1".equals(request.getOutputFormat())) {
gmlVersion = "3.1.1";
} else if ("text/xml; subtype=gml/3.2.1".equals(request.getOutputFormat()) ||
"text/xml; subtype=gml/3.2".equals(request.getOutputFormat()) ||
"application/gml+xml; version=3.2".equals(request.getOutputFormat())) {
gmlVersion = "3.2.1";
} else if ("application/json".equals(request.getOutputFormat())) {
gmlVersion = null;
} else {
throw new CstlServiceException("invalid outputFormat:" + request.getOutputFormat(), INVALID_PARAMETER_VALUE, "outputFormat");
}
/**
* 3 possibility here :
* 1) merge the collections
* 2) return a collection of collection.
* 3) if there is only one feature we return (change the return type in object)
*
* result TODO find an id and a member type
*/
final FeatureCollection featureCollection;
if (collections.size() > 1) {
featureCollection = FeatureStoreUtilities.sequence("collection-1", collections.toArray(new FeatureCollection[collections.size()]));
} else if (collections.size() == 1) {
featureCollection = collections.get(0);
} else {
featureCollection = FeatureStoreUtilities.collection("collection-1", null);
}
if (request.getResultType() == ResultTypeType.HITS) {
final XMLGregorianCalendar calendar;
try {
calendar = org.apache.sis.internal.jaxb.XmlUtilities.toXML(null, new Date());
} catch (DatatypeConfigurationException e) {
throw new CstlServiceException("Unable to create XMLGregorianCalendar from Date.");
}
return buildFeatureCollection(currentVersion, "collection-1", featureCollection.size(), calendar);
}
LOGGER.log(logLevel, "GetFeature treated in {0}ms", (System.currentTimeMillis() - start));
if(queries.size()==1 && queries.containsKey("urn:ogc:def:query:OGC-WFS::GetFeatureById")){
return new FeatureCollectionWrapper(featureCollection, schemaLocations, gmlVersion, currentVersion, (int)nbMatched,true);
}else{
return new FeatureCollectionWrapper(featureCollection, schemaLocations, gmlVersion, currentVersion, (int)nbMatched,false);
}
}
@Override
public Object getPropertyValue(final GetPropertyValue request) throws CstlServiceException {
LOGGER.log(logLevel, "GetPropertyValue request processing\n");
final long startTime = System.currentTimeMillis();
verifyBaseRequest(request, true, false);
final String valueReference = request.getValueReference();
if (valueReference == null) {
throw new CstlServiceException("ValueReference must be specified", MISSING_PARAMETER_VALUE, "valueReference");
} else if (valueReference.isEmpty()) {
throw new CstlServiceException("ValueReference must not be empty", INVALID_PARAMETER_VALUE, "valueReference");
}
final String userLogin = getUserLogin();
final Map<String, String> namespaceMapping = request.getPrefixMapping();
final String currentVersion = request.getVersion().toString();
final Collection<? extends Query> queries = extractStoredQueries(request).values();
final Integer maxFeatures = request.getCount();
final Map<String, String> schemaLocations = new HashMap<>();
final List<FeatureCollection> collections = new ArrayList<>();
for (final Query query : queries) {
final List<QName> typeNames;
final Map<String, QName> aliases = new HashMap<>();
if (query.getTypeNames().isEmpty()) {
typeNames = getConfigurationLayerNames(userLogin);
} else {
typeNames = query.getTypeNames();
if (!query.getAliases().isEmpty()) {
for (int i = 0; i < typeNames.size() && i < query.getAliases().size(); i++) {
aliases.put(query.getAliases().get(i), typeNames.get(i));
}
}
}
//decode filter-----------------------------------------------------
final Filter filter = extractJAXBFilter(query.getFilter(), Filter.INCLUDE, namespaceMapping, currentVersion);
//decode crs--------------------------------------------------------
final CoordinateReferenceSystem crs = extractCRS(query.getSrsName());
//decode property names---------------------------------------------
final List<String> requestPropNames = extractPropertyNames(query.getPropertyNames());
//decode sort by----------------------------------------------------
final List<SortBy> sortBys = visitJaxbSortBy(query.getSortBy(), namespaceMapping, currentVersion);
final QueryBuilder queryBuilder = new QueryBuilder();
queryBuilder.setCRS(crs);
if (!sortBys.isEmpty()) {
queryBuilder.setSortBy(sortBys.toArray(new SortBy[sortBys.size()]));
}
if (maxFeatures != 0){
queryBuilder.setMaxFeatures(maxFeatures);
}
for (QName typeName : typeNames) {
final GenericName fullTypeName = Utils.getNameFromQname(typeName);
if (layersContainsKey(userLogin, fullTypeName) == null) {
throw new CstlServiceException(UNKNOW_TYPENAME + typeName, INVALID_PARAMETER_VALUE, "typenames");
}
final Data layerD = getLayerReference(userLogin, fullTypeName);
if (!(layerD instanceof FeatureData)) {continue;}
final FeatureData layer = (FeatureData) layerD;
final FeatureType ft;
try {
ft = getFeatureTypeFromLayer(layer);
} catch (DataStoreException ex) {
throw new CstlServiceException(ex);
}
final Filter cleanFilter = processFilter(ft, filter, aliases);
// we ensure that the property names are contained in the feature type and add the mandatory attribute to the list
queryBuilder.setProperties(verifyPropertyNames(typeName, ft, requestPropNames));
queryBuilder.setFilter(cleanFilter);
queryBuilder.setTypeName(ft.getName());
queryBuilder.setHints(new Hints(HintsPending.FEATURE_HIDE_ID_PROPERTY, Boolean.TRUE));
// we verify that all the properties contained in the filter are known by the feature type.
verifyFilterProperty(ft, cleanFilter, aliases);
collections.add(layer.getStore().createSession(false).getFeatureCollection(queryBuilder.buildQuery()));
// we write The SchemaLocation
putSchemaLocation(typeName, schemaLocations, currentVersion);
}
}
/**
* 3 possibility here :
* 1) merge the collections
* 2) return a collection of collection.
* 3) if there is only one feature we return (change the return type in object)
*
* result TODO find an id and a member type
*/
final FeatureCollection featureCollection;
if (collections.size() > 1) {
featureCollection = FeatureStoreUtilities.sequence("collection-1", collections.toArray(new FeatureCollection[collections.size()]));
} else if (collections.size() == 1) {
featureCollection = collections.get(0);
} else {
featureCollection = FeatureStoreUtilities.collection("collection-1", null);
}
LOGGER.log(logLevel, "GetPropertyValue request processed in {0} ms", (System.currentTimeMillis() - startTime));
if (request.getResultType() == ResultTypeType.HITS) {
final XMLGregorianCalendar calendar;
try {
calendar = org.apache.sis.internal.jaxb.XmlUtilities.toXML(null, new Date());
} catch (DatatypeConfigurationException e) {
throw new CstlServiceException("Unable to create XMLGregorianCalendar from Date.");
}
return buildValueCollection(currentVersion, featureCollection.size(), calendar);
}
return new ValueCollectionWrapper(featureCollection, request.getValueReference(), "3.2.1");
}
private List<SortBy> visitJaxbSortBy(final org.geotoolkit.ogc.xml.SortBy jaxbSortby,final Map<String, String> namespaceMapping, final String version) {
if (jaxbSortby != null) {
final StyleXmlIO util = new StyleXmlIO();
if ("2.0.0".equals(version)) {
return util.getTransformer200(namespaceMapping).visitSortBy((org.geotoolkit.ogc.xml.v200.SortByType)jaxbSortby);
} else {
return util.getTransformer110(namespaceMapping).visitSortBy((org.geotoolkit.ogc.xml.v110.SortByType)jaxbSortby);
}
}
return new ArrayList<>();
}
/**
* {@inheritDoc }
*/
@Override
public AbstractGML getGMLObject(final GetGmlObject grbi) throws CstlServiceException {
throw new CstlServiceException("WFS get GML Object is not supported on this Constellation version.");
}
/**
* {@inheritDoc }
*/
@Override
public LockFeatureResponse lockFeature(final LockFeature gr) throws CstlServiceException {
throw new CstlServiceException("WFS Lock is not supported on this Constellation version.");
}
/**
* {@inheritDoc }
*/
@Override
public TransactionResponse transaction(final Transaction request) throws CstlServiceException {
LOGGER.log(logLevel, "Transaction request processing\n");
final long startTime = System.currentTimeMillis();
if (!isTransactionnal) {
throw new CstlServiceException("This method is not supported by this mode of WFS", OPERATION_NOT_SUPPORTED, "Request");
}
if (isTransactionSecurized() && !SecurityManagerHolder.getInstance().isAuthenticated()) {
throw new UnauthorizedException("You must be authentified to perform an transaction request.");
}
verifyBaseRequest(request, true, false);
// we prepare the report
final String userLogin = getUserLogin();
final String currentVersion = request.getVersion().toString();
int totalInserted = 0;
int totalUpdated = 0;
int totalDeleted = 0;
int totalReplaced = 0;
final List<Object> transactions = request.getTransactionAction();
final Map<String, String> inserted = new LinkedHashMap<>();
final Map<String, String> replaced = new LinkedHashMap<>();
final Map<String, String> namespaceMapping = request.getPrefixMapping();
final JAXPStreamFeatureReader featureReader = new JAXPStreamFeatureReader(getFeatureTypes(userLogin));
featureReader.getProperties().put(JAXPStreamFeatureReader.BINDING_PACKAGE, "GML");
for (Object transaction: transactions) {
/**
* Features insertion.
*/
if (transaction instanceof InsertElement) {
final InsertElement insertRequest = (InsertElement)transaction;
final String handle = insertRequest.getHandle();
// we verify the input format
if (insertRequest.getInputFormat() != null && !(insertRequest.getInputFormat().equals("text/xml; subtype=gml/3.1.1")
|| insertRequest.getInputFormat().equals("application/gml+xml; version=3.2"))) {
throw new CstlServiceException("This only input format supported are: text/xml; subtype=gml/3.1.1 and application/gml+xml; version=3.2",
INVALID_PARAMETER_VALUE, "inputFormat");
}
// what to do with the CRS ?
final CoordinateReferenceSystem insertCRS = extractCRS(insertRequest.getSrsName());
// what to do with that, which ones are supported ??
final IdentifierGenerationOptionType idGen = insertRequest.getIdgen();
for (Object featureObject : insertRequest.getFeature()) {
if (featureObject instanceof JAXBElement) {
featureObject = ((JAXBElement)featureObject).getValue();
}
FeatureType ft = null;
try {
if (featureObject instanceof Node) {
featureObject = featureReader.read(featureObject);
} else if (featureObject instanceof FeatureCollectionType) {
final FeatureCollectionType xmlCollection = (FeatureCollectionType) featureObject;
final String id = xmlCollection.getId();
final List<Feature> features = new ArrayList<>();
for (FeaturePropertyType fprop : xmlCollection.getFeatureMember()) {
Feature feat = (Feature)featureReader.read(fprop.getUnknowFeature());
ft = feat.getType();
features.add(feat);
}
featureObject = features;
}
} catch (IllegalArgumentException ex) {
throw new CstlServiceException(ex.getMessage(), ex, INVALID_VALUE);
} catch (IOException | XMLStreamException ex) {
throw new CstlServiceException(ex);
}
Collection<Feature> featureCollection;
if (featureObject instanceof Feature) {
final Feature feature = (Feature) featureObject;
ft = feature.getType();
featureCollection = Arrays.asList(feature);
} else if (featureObject instanceof List) {
featureCollection = (List) featureObject;
} else if (featureObject instanceof FeatureCollection) {
featureCollection = (Collection) featureObject;
ft = ((FeatureCollection)featureCollection).getFeatureType();
} else {
final String featureType;
if (featureObject == null) {
featureType = "null";
} else {
if (featureObject instanceof JAXBElement) {
featureType = "JAXBElement<" + ((JAXBElement)featureObject).getValue().getClass().getName() + ">";
} else {
featureType = featureObject.getClass().getName();
}
}
throw new CstlServiceException("Unexpected Object to insert:" + featureType);
}
GenericName typeName = ft.getName();
if (layersContainsKey(userLogin, typeName) == null) {
throw new CstlServiceException(UNKNOW_TYPENAME + typeName);
}
final FeatureData layer = (FeatureData) getLayerReference(userLogin, typeName);
try {
final CoordinateReferenceSystem trueCrs = layer.getStore().getFeatureType(typeName).getCoordinateReferenceSystem();
if(trueCrs != null && !Utilities.equalsIgnoreMetadata(trueCrs, ft.getCoordinateReferenceSystem())){
final FeatureCollection collection = FeatureStoreUtilities.collection(ft,featureCollection);
featureCollection = GenericReprojectFeatureIterator.wrap(collection, trueCrs);
}
final List<FeatureId> features = layer.getStore().addFeatures(typeName, featureCollection);
for (FeatureId fid : features) {
inserted.put(fid.getID(), handle);// get the id of the inserted feature
totalInserted++;
LOGGER.log(Level.FINER, "fid inserted: {0} total:{1}", new Object[]{fid, totalInserted});
}
} catch (DataStoreException ex) {
Logging.unexpectedException(LOGGER, ex);
} catch (ClassCastException ex) {
Logging.unexpectedException(LOGGER, ex);
throw new CstlServiceException("The specified Datastore does not suport the write operations.");
}
}
/**
* Features remove.
*/
} else if (transaction instanceof DeleteElement) {
final DeleteElement deleteRequest = (DeleteElement) transaction;
//decode filter-----------------------------------------------------
if (deleteRequest.getFilter() == null) {
throw new CstlServiceException("The filter must be specified.", MISSING_PARAMETER_VALUE, "filter");
}
final Filter filter = extractJAXBFilter(deleteRequest.getFilter(), Filter.EXCLUDE, namespaceMapping, currentVersion);
final GenericName typeName = Utils.getNameFromQname(deleteRequest.getTypeName());
if (layersContainsKey(userLogin, typeName) == null) {
throw new CstlServiceException(UNKNOW_TYPENAME + typeName, INVALID_PARAMETER_VALUE, "typename");
}
final FeatureData layer = (FeatureData) getLayerReference(userLogin, typeName);
try {
final FeatureType ft = getFeatureTypeFromLayer(layer);
final Filter cleanFilter = processFilter(ft, filter, null);
// we verify that all the properties contained in the filter are known by the feature type.
verifyFilterProperty(ft, cleanFilter, null);
// we extract the number of feature deleted
final QueryBuilder queryBuilder = new QueryBuilder(layer.getName());
queryBuilder.setFilter(cleanFilter);
totalDeleted = totalDeleted + (int) layer.getStore().getCount(queryBuilder.buildQuery());
layer.getStore().removeFeatures(layer.getName(), filter);
} catch (DataStoreException ex) {
throw new CstlServiceException(ex);
} catch (ClassCastException ex) {
Logging.unexpectedException(LOGGER, ex);
throw new CstlServiceException("The specified Datastore does not suport the delete operations.");
}
/**
* Features updates.
*/
} else if (transaction instanceof UpdateElement) {
final UpdateElement updateRequest = (UpdateElement) transaction;
// we verify the input format
if (updateRequest.getInputFormat() != null && !(updateRequest.getInputFormat().equals("text/xml; subtype=gml/3.1.1")
|| updateRequest.getInputFormat().equals("application/gml+xml; version=3.2"))) {
throw new CstlServiceException("This only input format supported are: text/xml; subtype=gml/3.1.1 and application/gml+xml; version=3.2",
INVALID_PARAMETER_VALUE, "inputFormat");
}
//decode filter-----------------------------------------------------
final Filter filter = extractJAXBFilter(updateRequest.getFilter(),Filter.EXCLUDE, namespaceMapping, currentVersion);
//decode crs--------------------------------------------------------
final CoordinateReferenceSystem crs = extractCRS(updateRequest.getSrsName());
final GenericName typeName = Utils.getNameFromQname(updateRequest.getTypeName());
if (layersContainsKey(userLogin, typeName) == null) {
throw new CstlServiceException(UNKNOW_TYPENAME + typeName, INVALID_PARAMETER_VALUE, "typename");
}
final FeatureData layer = (FeatureData) getLayerReference(userLogin, typeName);
try {
final FeatureType ft = getFeatureTypeFromLayer(layer);
if (ft == null) {
throw new CstlServiceException("Unable to find the featuretype:" + layer.getName());
}
final Map<String,Object> values = new HashMap<>();
// we verify that the update property are contained in the feature type
for (final Property updateProperty : updateRequest.getProperty()) {
String updatePropertyName = updateProperty.getLocalName();
Binding pa = Bindings.getBinding(FeatureType.class, updatePropertyName);
if (pa == null || pa.get(ft, updatePropertyName, null) == null) {
throw new CstlServiceException("The feature Type " + updateRequest.getTypeName() + " does not has such a property: " + updatePropertyName, INVALID_VALUE);
}
PropertyDescriptor propertyDesc = (PropertyDescriptor) pa.get(ft, updatePropertyName, null);
PropertyType propertyType = propertyDesc.getType();
if (propertyType instanceof ComplexType && updateProperty.getValue() != null) {
ComplexType ct = (ComplexType) propertyType;
if (ct.getDescriptor("_value") != null) {
updatePropertyName = "/" + updatePropertyName + "/_value";
propertyType = ct.getDescriptor("_value").getType();
}
}
Object value;
if (updateProperty.getValue() instanceof Element) {
final String strValue = getXMLFromElementNSImpl((Element)updateProperty.getValue());
value = null;
LOGGER.log(Level.FINER, ">> updating : {0} => {1}", new Object[]{updatePropertyName, strValue});
} else {
value = updateProperty.getValue();
if (value instanceof AbstractGeometryType) {
try {
final String defaultCRS = getCRSCode(ft);
final CoordinateReferenceSystem exposedCrs = CRS.decode(defaultCRS);
final CoordinateReferenceSystem trueCrs = ((GeometryType)propertyType).getCoordinateReferenceSystem();
value = GeometrytoJTS.toJTS((AbstractGeometryType) value);
if(trueCrs != null && !CRS.equalsIgnoreMetadata(exposedCrs, trueCrs)){
value = JTS.transform((Geometry)value, CRS.findMathTransform(exposedCrs, trueCrs));
}
} catch (TransformException | FactoryException ex) {
Logging.unexpectedException(LOGGER, ex);
} catch (IllegalArgumentException ex) {
throw new CstlServiceException(ex);
}
} else if (value instanceof DirectPosition) {
final DirectPosition dp = (DirectPosition) value;
value = new GeometryFactory().createPoint(new Coordinate(dp.getOrdinate(0), dp.getOrdinate(1)));
} else if (value instanceof String) {
value = featureReader.readValue((String) value, propertyType);
}
LOGGER.log(Level.FINER, ">> updating : {0} => {1}", new Object[]{updatePropertyName, value});
if (value != null) {
LOGGER.log(Level.FINER, "type : {0}", value.getClass());
}
}
values.put(updatePropertyName, value);
}
final Filter cleanFilter = processFilter(ft, filter, null);
// we verify that all the properties contained in the filter are known by the feature type.
verifyFilterProperty(ft, cleanFilter, null);
// we extract the number of feature update
final QueryBuilder queryBuilder = new QueryBuilder(layer.getName());
queryBuilder.setFilter(cleanFilter);
totalUpdated = totalUpdated + (int) layer.getStore().getCount(queryBuilder.buildQuery());
FeatureWriter fw = layer.getStore().getFeatureWriter(layer.getName(), filter);
try {
while (fw.hasNext()) {
Feature feat = fw.next();
for (Entry<String, Object> entry : values.entrySet()) {
Binding pa = Bindings.getBinding(Feature.class, entry.getKey());
pa.set(feat, entry.getKey(), entry.getValue());
}
fw.write();
}
} finally {
fw.close();
}
} catch (DataStoreException ex) {
throw new CstlServiceException(ex);
}
} else if (transaction instanceof ReplaceElement) {
final ReplaceElement replaceRequest = (ReplaceElement) transaction;
final String handle = replaceRequest.getHandle();
// we verify the input format
if (replaceRequest.getInputFormat() != null && !(replaceRequest.getInputFormat().equals("text/xml; subtype=gml/3.1.1")
|| replaceRequest.getInputFormat().equals("application/gml+xml; version=3.2"))) {
throw new CstlServiceException("This only input format supported are: text/xml; subtype=gml/3.1.1 and application/gml+xml; version=3.2",
INVALID_PARAMETER_VALUE, "inputFormat");
}
//decode filter-----------------------------------------------------
final Filter filter = extractJAXBFilter(replaceRequest.getFilter(),Filter.EXCLUDE, namespaceMapping, currentVersion);
//decode crs--------------------------------------------------------
final CoordinateReferenceSystem crs = extractCRS(replaceRequest.getSrsName());
// extract replacement feature
Object featureObject = replaceRequest.getFeature();
if (featureObject instanceof JAXBElement) {
featureObject = ((JAXBElement) featureObject).getValue();
}
try {
if (featureObject instanceof Node) {
featureObject = featureReader.read(featureObject);
} else if (featureObject instanceof FeatureCollectionType) {
final FeatureCollectionType xmlCollection = (FeatureCollectionType) featureObject;
final String id = xmlCollection.getId();
final List<Feature> features = new ArrayList<>();
FeatureType ft = null;
for (FeaturePropertyType fprop : xmlCollection.getFeatureMember()) {
Feature feat = (Feature) featureReader.read(fprop.getUnknowFeature());
ft = feat.getType();
features.add(feat);
}
final FeatureCollection collection = FeatureStoreUtilities.collection(id, ft);
collection.addAll(features);
featureObject = collection;
}
} catch (IllegalArgumentException ex) {
throw new CstlServiceException(ex.getMessage(), ex, INVALID_PARAMETER_VALUE);
} catch (IOException | XMLStreamException ex) {
throw new CstlServiceException(ex);
}
final GenericName typeName;
FeatureCollection featureCollection;
if (featureObject instanceof Feature) {
final Feature feature = (Feature) featureObject;
typeName = feature.getType().getName();
featureCollection = FeatureStoreUtilities.collection(feature);
} else if (featureObject instanceof FeatureCollection) {
featureCollection = (FeatureCollection) featureObject;
typeName = ((FeatureCollection) featureCollection).getFeatureType().getName();
} else {
final String featureType;
if (featureObject == null) {
featureType = "null";
} else {
if (featureObject instanceof JAXBElement) {
featureType = "JAXBElement<" + ((JAXBElement) featureObject).getValue().getClass().getName() + ">";
} else {
featureType = featureObject.getClass().getName();
}
}
throw new CstlServiceException("Unexpected replacement object:" + featureType);
}
if (layersContainsKey(userLogin, typeName) == null) {
throw new CstlServiceException(UNKNOW_TYPENAME + typeName);
}
try {
final FeatureData layer = (FeatureData) getLayerReference(userLogin, typeName);
final FeatureType ft = getFeatureTypeFromLayer(layer);
// we extract the number of feature to replace
final QueryBuilder queryBuilder = new QueryBuilder(layer.getName());
queryBuilder.setFilter(processFilter(ft, filter, null));
totalReplaced = totalReplaced + (int) layer.getStore().getCount(queryBuilder.buildQuery());
// first remove the feature to replace
layer.getStore().removeFeatures(layer.getName(), filter);
// then add the new one
final CoordinateReferenceSystem trueCrs = layer.getStore().getFeatureType(typeName).getCoordinateReferenceSystem();
if(trueCrs != null && !CRS.equalsIgnoreMetadata(trueCrs, featureCollection.getFeatureType().getCoordinateReferenceSystem())){
featureCollection = GenericReprojectFeatureIterator.wrap(featureCollection, trueCrs);
}
final List<FeatureId> features = layer.getStore().addFeatures(typeName, featureCollection);
for (FeatureId fid : features) {
replaced.put(fid.getID(), handle);// get the id of the replaced feature
LOGGER.log(Level.FINER, "fid inserted: {0} total:{1}", new Object[]{fid, totalInserted});
}
} catch (DataStoreException ex) {
throw new CstlServiceException(ex);
}
} else {
String className = " null object";
if (transaction != null) {
className = transaction.getClass().getName();
}
throw new CstlServiceException("This kind of transaction is not supported by the service: " + className,
INVALID_PARAMETER_VALUE, "transaction");
}
}
final TransactionResponse response = buildTransactionResponse(currentVersion,
totalInserted,
totalUpdated,
totalDeleted,
totalReplaced,
inserted,
replaced);
LOGGER.log(logLevel, "Transaction request processed in {0} ms", (System.currentTimeMillis() - startTime));
return response;
}
/**
* Extract the a XML string from a W3C Element.
*
* @param node An W3c Xml Element.
*
* @return a string containing the xml representation.
*/
private String getXMLFromElementNSImpl(final Element elt) {
final StringBuilder s = new StringBuilder();
s.append('<').append(elt.getLocalName()).append('>');
final Node node = elt.getFirstChild();
s.append(getXMLFromNode(node));
s.append("</").append(elt.getLocalName()).append('>');
return s.toString();
}
/**
* Extract the a XML string from a W3C node.
*
* @param node An W3c Xml node.
*
* @return a string builder containing the xml.
*/
private StringBuilder getXMLFromNode(final Node node) {
final StringBuilder temp = new StringBuilder();
if (!node.getNodeName().equals("#text")){
temp.append("<").append(node.getNodeName());
final NamedNodeMap attrs = node.getAttributes();
for(int i=0;i<attrs.getLength();i++){
temp.append(" ").append(attrs.item(i).getNodeName()).append("=\"").append(attrs.item(i).getTextContent()).append("\" ");
}
temp.append(">");
}
if (node.hasChildNodes()) {
final NodeList nodes = node.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
temp.append(getXMLFromNode(nodes.item(i)));
}
}
else{
temp.append(node.getTextContent());
}
if (!node.getNodeName().equals("#text")) {temp.append("</").append(node.getNodeName()).append(">");}
return temp;
}
/**
* Extract an OGC filter usable by the dataStore from the request filter
* unmarshalled by JAXB.
*
* @param jaxbFilter an OGC JAXB filter.
* @return An OGC filter
* @throws CstlServiceException
*/
private Filter extractJAXBFilter(final Filter jaxbFilter, final Filter defaultFilter, final Map<String, String> namespaceMapping, final String currentVersion) throws CstlServiceException {
final StyleXmlIO util = new StyleXmlIO();
final Filter filter;
try {
if (jaxbFilter != null) {
if ("2.0.0".equals(currentVersion)) {
filter = util.getTransformer200(namespaceMapping).visitFilter((org.geotoolkit.ogc.xml.v200.FilterType)jaxbFilter);
} else {
filter = util.getTransformer110(namespaceMapping).visitFilter((org.geotoolkit.ogc.xml.v110.FilterType)jaxbFilter);
}
} else {
filter = defaultFilter;
}
} catch (Exception ex) {
throw new CstlServiceException(ex, INVALID_PARAMETER_VALUE);
}
return filter;
}
/**
* Return a coordinate reference system from an identifier.
*
* @param srsName a CRS identifier.
* @return
* @throws CstlServiceException
*/
private CoordinateReferenceSystem extractCRS(final String srsName) throws CstlServiceException {
final CoordinateReferenceSystem crs;
if (srsName != null) {
try {
crs = CRS.decode(srsName, false);
//todo use other properties to filter properly
} catch (NoSuchAuthorityCodeException ex) {
throw new CstlServiceException(ex, INVALID_PARAMETER_VALUE);
} catch (FactoryException ex) {
throw new CstlServiceException(ex, INVALID_PARAMETER_VALUE);
}
} else {
crs = null;
}
return crs;
}
/**
* Verify that all the property contained in the filter are known by the featureType
*
* @param ft A featureType.
* @param filter An OGC filter.
*
* @throws CstlServiceException if one of the propertyName in the filter is not present in the featureType.
*/
private void verifyFilterProperty(final FeatureType ft, final Filter filter, final Map<String, QName> aliases) throws CstlServiceException {
final Collection<String> filterProperties = (Collection<String>) filter.accept(ListingPropertyVisitor.VISITOR, null);
if (filterProperties != null) {
for (String filterProperty : filterProperties) {
if (filterProperty.startsWith("@")){
//this property in an id property, we won't find it in the feature type
//but it always exist on the features
continue;
}
// look to remove featureType prefix
String ftName = "";
if (NamesExt.getNamespace(ft.getName()) != null) {
ftName = "{" + NamesExt.getNamespace(ft.getName()) + "}";
}
ftName = ftName + ft.getName().tip().toString();
if (filterProperty.startsWith(ftName)) {
filterProperty = filterProperty.substring(ftName.length());
}
if (aliases != null) {
for (String entry : aliases.keySet()) {
if (filterProperty.startsWith(entry + "/")) {
filterProperty = filterProperty.substring(entry.length());
}
}
}
final Binding pa = Bindings.getBinding(FeatureType.class, filterProperty);
if (pa == null || pa.get(ft, filterProperty, null) == null) {
String s = "";
if (NamesExt.getNamespace(ft.getName()) != null) {
s = "{" + NamesExt.getNamespace(ft.getName()) + "}";
}
s = s + ft.getName().tip().toString();
throw new CstlServiceException("The feature Type " + s + " does not has such a property: " + filterProperty, INVALID_PARAMETER_VALUE, "filter");
}
}
}
if (!((Boolean)filter.accept(new IsValidSpatialFilterVisitor(ft), null))) {
throw new CstlServiceException("The filter try to apply spatial operators on non-spatial property", INVALID_PARAMETER_VALUE, "filter");
}
}
/**
* Ensure crs is set on all geometric elements and with correct crs.
* replace Aliases by correct feature type names.
* remove feature type name prefixing propertyName.
*/
private Filter processFilter(final FeatureType ft, Filter filter, final Map<String, QName> aliases){
try {
final String defaultCRS = getCRSCode(ft);
final CoordinateReferenceSystem exposedCrs = CRS.decode(defaultCRS);
final CoordinateReferenceSystem trueCrs = ft.getCoordinateReferenceSystem();
filter = (Filter) filter.accept(new AliasFilterVisitor(aliases), null);
filter = (Filter) filter.accept(new UnprefixerFilterVisitor(ft), null);
filter = (Filter) filter.accept(new DefaultGeomPropertyVisitor(ft), null);
filter = (Filter) filter.accept(new GMLNamespaceVisitor(), null);
filter = (Filter) filter.accept(new BooleanVisitor(ft), null);
if (exposedCrs!=null && trueCrs!=null && !CRS.equalsIgnoreMetadata(trueCrs, exposedCrs)) {
filter = (Filter) filter.accept(FillCrsVisitor.VISITOR, exposedCrs);
filter = (Filter) filter.accept(new CrsAdjustFilterVisitor(exposedCrs, trueCrs), null);
}
} catch (FactoryException ex) {
LOGGER.log(Level.WARNING, ex.getMessage(), ex);
}
return filter;
}
/**
* Extract the WGS84 BBOx from a featureSource.
* what ? may not be wgs84 exactly ? why is there a CRS attribute on a wgs84 bbox ?
*/
private static Object toBBox(final FeatureStore source, final GenericName groupName, final String version) throws CstlServiceException{
try {
Envelope env = source.getEnvelope(QueryBuilder.all(groupName));
final CoordinateReferenceSystem epsg4326 = CRS.decode("urn:ogc:def:crs:OGC:2:84");
if (env != null) {
if (!CRS.equalsIgnoreMetadata(env.getCoordinateReferenceSystem(), epsg4326)) {
env = CRS.transform(env, epsg4326);
}
return buildBBOX(version,
"urn:ogc:def:crs:OGC:2:84",
env.getMinimum(0),
env.getMinimum(1),
env.getMaximum(0),
env.getMaximum(1));
} else {
return buildBBOX(version,"urn:ogc:def:crs:OGC:2:84", -180, -90, 180, 90);
}
} catch (DataStoreException | TransformException | FactoryException ex) {
throw new CstlServiceException(ex);
}
}
private static void applyParameterOnQuery(final Query query, final List<? extends Parameter> parameters) throws CstlServiceException {
applyParameterOnFilter(query.getFilter(), parameters);
final List<QName> toRemove = new ArrayList<>();
final List<QName> toAdd = new ArrayList<>();
if (query.getTypeNames().isEmpty()) {
for (Parameter param : parameters) {
if (!param.getContent().isEmpty() && param.getContent().get(0) instanceof QName && param.getName().equalsIgnoreCase("typeName")) {
toAdd.add((QName)param.getContent().get(0));
}
}
} else {
for (QName q : query.getTypeNames()) {
for (Parameter param : parameters) {
if (q.getLocalPart().contains("$" + param.getName())) {
toRemove.add(q);
if (!param.getContent().isEmpty() && param.getContent().get(0) instanceof QName) {
toAdd.add((QName)param.getContent().get(0));
} else {
LOGGER.warning("bad type or empty parameter content");
}
}
}
}
}
query.getTypeNames().removeAll(toRemove);
query.getTypeNames().addAll(toAdd);
}
private static void applyParameterOnFilter(final Filter filter, final List<? extends Parameter> parameters) throws CstlServiceException {
final Object filterObject;
if (filter instanceof XMLFilter) {
filterObject = ((XMLFilter)filter).getFilterObject();
} else {
filterObject = filter;
}
if (filterObject instanceof BBOXType) {
final BBOXType bb = (BBOXType) filterObject;
if (bb.getAny() != null && bb.getAny() instanceof String) {
String s = (String)bb.getAny();
for (Parameter param : parameters) {
if (s.contains("${" + param.getName() + '}')) {
bb.setAny(param.getContent().get(0));
}
}
}
} else if (filterObject instanceof BinarySpatialOperator) {
final BinarySpatialOperator binary = (BinarySpatialOperator) filterObject;
if (binary.getExpression2() != null && binary.getExpression2() instanceof XMLLiteral) {
final XMLLiteral lit = (XMLLiteral) binary.getExpression2();
if (lit.getValue() instanceof String) {
String s = (String)lit.getValue();
for (Parameter param : parameters) {
if (s.contains("${" + param.getName() + '}')) {
s = s.replace("${" + param.getName()+ '}', (String)param.getContent().get(0));
}
}
lit.getContent().clear();
lit.setContent(s);
}
}
} else if (filterObject instanceof BinaryComparisonOperator) {
final BinaryComparisonOperator binary = (BinaryComparisonOperator) filterObject;
if (binary.getExpression2() != null && binary.getExpression2() instanceof XMLLiteral) {
final XMLLiteral lit = (XMLLiteral) binary.getExpression2();
if (lit.getValue() instanceof String) {
String s = (String)lit.getValue();
for (Parameter param : parameters) {
if (s.contains("${" + param.getName()+ '}')) {
s = s.replace("${" + param.getName()+ '}', (String)param.getContent().get(0));
}
}
lit.getContent().clear();
lit.setContent(s);
}
}
} else if (filterObject instanceof BinaryLogicOperator) {
final BinaryLogicOperator binary = (BinaryLogicOperator) filterObject;
for (Filter child : binary.getChildren()) {
applyParameterOnFilter(child, parameters);
}
} else if (filter != null) {
throw new CstlServiceException("Unimplemented filter implementation:" + filterObject.getClass().getName(), NO_APPLICABLE_CODE);
}
}
/**
* Verify that the bases request attributes are correct.
*
* @param request an object request with the base attribute (all except GetCapabilities request);
*/
private void verifyBaseRequest(final RequestBase request, final boolean versionMandatory, final boolean getCapabilities) throws CstlServiceException {
isWorking();
if (request != null) {
if (request.getService() != null) {
if (request.getService().isEmpty()) {
// we let pass (CITE test)
} else if (!request.getService().equalsIgnoreCase("WFS")) {
throw new CstlServiceException("service must be \"WFS\"!",
INVALID_PARAMETER_VALUE, "service");
}
} else {
throw new CstlServiceException("service must be specified!",
MISSING_PARAMETER_VALUE, "service");
}
if (request.getVersion() != null) {
if (isSupportedVersion(request.getVersion().toString())) {
request.setVersion(request.getVersion().toString());
// for the CITE test
} else if (request.getVersion().toString().isEmpty()) {
request.setVersion(ServiceDef.WFS_1_1_0.version.toString());
} else {
final CodeList code;
if (getCapabilities) {
code = VERSION_NEGOTIATION_FAILED;
} else {
code = INVALID_PARAMETER_VALUE;
}
throw new CstlServiceException("version must be \"1.1.0\" or \"2.0.0\"!", code, "version");
}
} else {
if (versionMandatory) {
throw new CstlServiceException("version must be specified!", MISSING_PARAMETER_VALUE, "version");
} else {
request.setVersion(ServiceDef.WFS_1_1_0.version.toString());
}
}
} else {
throw new CstlServiceException("The request is null!", NO_APPLICABLE_CODE);
}
}
private List<FeatureType> getFeatureTypes(final String userLogin) throws CstlServiceException {
final List<FeatureType> types = new ArrayList<>();
//search all types
for (final QName name : getConfigurationLayerNames(userLogin)) {
final Data layer = getLayerReference(userLogin, name);
if (!(layer instanceof FeatureData)) {continue;}
try {
//fix feature type to define the exposed crs : true EPSG axis order
final FeatureType baseType = getFeatureTypeFromLayer((FeatureData)layer);
final String crsCode = getCRSCode(baseType);
final CoordinateReferenceSystem exposedCrs = CRS.decode(crsCode);
final FeatureType exposedType = FeatureTypeUtilities.transform(baseType, exposedCrs);
types.add(exposedType);
} catch (Exception ex) {
LOGGER.severe("DataStore exception while getting featureType");
}
}
return types;
}
@Override
public ListStoredQueriesResponse listStoredQueries(final ListStoredQueries request) throws CstlServiceException {
LOGGER.log(logLevel, "ListStoredQueries request processing\n");
final long startTime = System.currentTimeMillis();
verifyBaseRequest(request, true, false);
final String currentVersion = request.getVersion().toString();
final ListStoredQueriesResponse response = buildListStoredQueriesResponse(currentVersion, storedQueries);
LOGGER.log(logLevel, "ListStoredQueries request processed in {0} ms", (System.currentTimeMillis() - startTime));
return response;
}
@Override
public DescribeStoredQueriesResponse describeStoredQueries(final DescribeStoredQueries request) throws CstlServiceException {
LOGGER.log(logLevel, "DescribeStoredQueries request processing\n");
final long startTime = System.currentTimeMillis();
verifyBaseRequest(request, true, false);
final String currentVersion = request.getVersion().toString();
final List<StoredQueryDescription> storedQueryList;
if (request.getStoredQueryId() != null && !request.getStoredQueryId().isEmpty()) {
storedQueryList = new ArrayList<>();
for (String id : request.getStoredQueryId()) {
for (StoredQueryDescription description : storedQueries) {
if (description.getId().equals(id)) {
storedQueryList.add(description);
}
}
}
} else {
storedQueryList = storedQueries;
}
final DescribeStoredQueriesResponse response = buildDescribeStoredQueriesResponse(currentVersion, storedQueryList);
LOGGER.log(logLevel, "DescribeStoredQueries request processed in {0} ms", (System.currentTimeMillis() - startTime));
return response;
}
@Override
public CreateStoredQueryResponse createStoredQuery(final CreateStoredQuery request) throws CstlServiceException {
LOGGER.log(logLevel, "CreateStoredQuery request processing\n");
final long startTime = System.currentTimeMillis();
verifyBaseRequest(request, true, false);
final String currentVersion = request.getVersion().toString();
storedQueries.addAll(request.getStoredQueryDefinition());
storedQueries();
final CreateStoredQueryResponse response = buildCreateStoredQueryResponse(currentVersion, "OK");
LOGGER.log(logLevel, "CreateStoredQuery request processed in {0} ms", (System.currentTimeMillis() - startTime));
return response;
}
@Override
public DropStoredQueryResponse dropStoredQuery(final DropStoredQuery request) throws CstlServiceException {
LOGGER.log(logLevel, "dropStoredQuery request processing\n");
final long startTime = System.currentTimeMillis();
verifyBaseRequest(request, true, false);
final String currentVersion = request.getVersion().toString();
StoredQueryDescription candidate = null;
for (StoredQueryDescription sq : storedQueries) {
if (sq.getId().equals(request.getId())) {
candidate = sq;
}
}
if (candidate == null) {
throw new CstlServiceException("Unexisting Stored query: " + request.getId(), INVALID_PARAMETER_VALUE);
} else {
storedQueries.remove(candidate);
}
storedQueries();
final DropStoredQueryResponse response = buildDropStoredQueryResponse(currentVersion, "OK");
LOGGER.log(logLevel, "dropStoredQuery request processed in {0} ms", (System.currentTimeMillis() - startTime));
return response;
}
@Override
public List<ParameterExpression> getParameterForStoredQuery(final String queryId) {
final List<ParameterExpression> results = new ArrayList<>();
for (StoredQueryDescription description : storedQueries) {
if (description.getId().equals(queryId)) {
results.addAll(description.getParameter());
}
}
return results;
}
}