/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.wfs.v2_0; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; import java.net.URLEncoder; import java.util.Map; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.Executors; import javax.xml.namespace.QName; import org.apache.commons.codec.binary.Base64; import org.custommonkey.xmlunit.XMLAssert; import org.geoserver.catalog.Catalog; import org.geoserver.catalog.DataStoreInfo; import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.catalog.NamespaceInfo; import org.geoserver.config.GeoServer; import org.geoserver.data.test.CiteTestData; import org.geoserver.data.test.SystemTestData; import org.geoserver.wfs.GMLInfo; import org.geoserver.wfs.WFSInfo; import org.geotools.gml3.v3_2.GML; import org.junit.Ignore; import org.junit.Test; import org.springframework.mock.web.MockHttpServletResponse; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public class DescribeFeatureTypeTest extends WFS20TestSupport { // @Override // protected String getLogConfiguration() { // return "/DEFAULT_LOGGING.properties"; // } @Override protected void setUpInternal(SystemTestData dataDirectory) throws Exception { DataStoreInfo di = getCatalog().getDataStoreByName(CiteTestData.CITE_PREFIX); di.setEnabled(false); getCatalog().save(di); } @Override protected void setUpNamespaces(Map<String, String> namespaces) { super.setUpNamespaces(namespaces); namespaces.put("soap", "http://www.w3.org/2003/05/soap-envelope"); } @Test public void testGet() throws Exception { String typeName = getLayerId(CiteTestData.PRIMITIVEGEOFEATURE); Document doc = getAsDOM( "wfs?service=WFS&version=2.0.0&request=DescribeFeatureType&typeName=" + typeName); assertSchema(doc, CiteTestData.PRIMITIVEGEOFEATURE); } @Test public void testConcurrentGet() throws Exception { String typeName = getLayerId(CiteTestData.PRIMITIVEGEOFEATURE); ExecutorCompletionService<Object> es = new ExecutorCompletionService<>(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())); final int REQUESTS = 200; for (int i = 0; i < REQUESTS; i++) { es.submit(() -> { Document doc = getAsDOM( "wfs?service=WFS&version=2.0.0&request=DescribeFeatureType&typeName=" + typeName); assertSchema(doc, CiteTestData.PRIMITIVEGEOFEATURE); return null; }); } // just check there are no exceptions for (int i = 0; i < REQUESTS; i++) { es.take().get(); } } @Test public void testPost() throws Exception { String typeName = getLayerId(CiteTestData.PRIMITIVEGEOFEATURE); String xml = "<wfs:DescribeFeatureType service='WFS' version='2.0.0' " + "xmlns:wfs='http://www.opengis.net/wfs/2.0' " + "xmlns:sf='" + CiteTestData.PRIMITIVEGEOFEATURE.getNamespaceURI() + "'>" + " <wfs:TypeName>" + typeName + "</wfs:TypeName>" + "</wfs:DescribeFeatureType>"; Document doc = postAsDOM("wfs", xml); assertSchema(doc, CiteTestData.PRIMITIVEGEOFEATURE); } @Test public void testConcurrentPost() throws Exception { String typeName = getLayerId(CiteTestData.PRIMITIVEGEOFEATURE); ExecutorCompletionService<Object> es = new ExecutorCompletionService<>(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())); final int REQUESTS = 200; for (int i = 0; i < REQUESTS; i++) { es.submit(() -> { Document doc = getAsDOM( "wfs?service=WFS&version=2.0.0&request=DescribeFeatureType&typeName=" + typeName); assertSchema(doc, CiteTestData.PRIMITIVEGEOFEATURE); return null; }); } // just check there are no exceptions for (int i = 0; i < REQUESTS; i++) { long start = System.currentTimeMillis(); es.take().get(); if(i % 100 == 0) { long curr = System.currentTimeMillis(); LOGGER.info(i + " - " + (curr - start)); start = curr; } } } void assertSchema(Document doc, QName... types) throws Exception { assertEquals("xsd:schema", doc.getDocumentElement().getNodeName()); XMLAssert.assertXpathExists("//xsd:import[@namespace='" + GML.NAMESPACE + "']", doc); for (QName type : types) { String eName = type.getLocalPart(); String tName = eName + "Type"; XMLAssert.assertXpathEvaluatesTo("1", "count(//xsd:complexType[@name='" + tName + "'])", doc); XMLAssert.assertXpathEvaluatesTo("1", "count(//xsd:element[@name='" + eName + "'])", doc); } } @Test public void testDateMappings() throws Exception { String typeName = getLayerId(CiteTestData.PRIMITIVEGEOFEATURE); String xml = "<wfs:DescribeFeatureType service='WFS' version='2.0.0' " + "xmlns:wfs='http://www.opengis.net/wfs/2.0' " + "xmlns:sf='" + CiteTestData.PRIMITIVEGEOFEATURE.getNamespaceURI() + "'>" + " <wfs:TypeName>" + typeName + "</wfs:TypeName>" + "</wfs:DescribeFeatureType>"; Document doc = postAsDOM("wfs", xml); assertSchema(doc, CiteTestData.PRIMITIVEGEOFEATURE); NodeList elements = doc.getElementsByTagName("xsd:element"); boolean date = false; boolean dateTime = false; for (int i = 0; i < elements.getLength(); i++) { Element e = (Element) elements.item(i); if ("dateProperty".equals(e.getAttribute("name"))) { date = "xsd:date".equals(e.getAttribute("type")); } if ("dateTimeProperty".equals(e.getAttribute("name"))) { dateTime = "xsd:dateTime".equals(e.getAttribute("type")); } } assertTrue(date); assertTrue(dateTime); } @Test public void testNoNamespaceDeclaration() throws Exception { String typeName = getLayerId(CiteTestData.PRIMITIVEGEOFEATURE); String xml = "<wfs:DescribeFeatureType service='WFS' version='2.0.0' " + "xmlns:wfs='http://www.opengis.net/wfs/2.0'>" + " <wfs:TypeName>" + typeName + "</wfs:TypeName>" + "</wfs:DescribeFeatureType>"; Document doc = postAsDOM("wfs", xml); // with previous code missing namespace would have resulted in a service exception assertSchema(doc, CiteTestData.PRIMITIVEGEOFEATURE); } @Test public void testMultipleTypesImport() throws Exception { String typeName1 = getLayerId(CiteTestData.PRIMITIVEGEOFEATURE); String typeName2 = getLayerId(CiteTestData.GENERICENTITY); String xml = "<wfs:DescribeFeatureType service='WFS' version='2.0.0' " + "xmlns:wfs='http://www.opengis.net/wfs/2.0'>" + " <wfs:TypeName>" + typeName1 + "</wfs:TypeName>" + " <wfs:TypeName>" + typeName2 + "</wfs:TypeName>" + "</wfs:DescribeFeatureType>"; Document doc = postAsDOM("wfs", xml); assertSchema(doc, CiteTestData.PRIMITIVEGEOFEATURE, CiteTestData.GENERICENTITY); NodeList nodes = doc.getDocumentElement().getChildNodes(); boolean seenComplexType = false; for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); if (node.getNodeName().equals("xsd:complexType")) { seenComplexType = true; } else if (seenComplexType && node.getNodeName().equals("xsd:import")) { fail("All xsd:import must occur before all xsd:complexType"); } } } /** * See https://osgeo-org.atlassian.net/browse/GEOS-3306 * */ @Test public void testUserSuppliedTypeNameNamespace() throws Exception { final QName typeName = CiteTestData.POLYGONS; String path = "ows?service=WFS&version=2.0.0&request=DescribeFeatureType&" + "typeName=myPrefix:" + typeName.getLocalPart() + "&namespace=xmlns(myPrefix%3D" + URLEncoder.encode(typeName.getNamespaceURI(), "UTF-8") + ")"; Document doc = getAsDOM(path); assertSchema(doc, CiteTestData.POLYGONS); } /** * See https://osgeo-org.atlassian.net/browse/GEOS-3306 * */ @Test public void testUserSuppliedTypeNameDefaultNamespace() throws Exception { final QName typeName = CiteTestData.POLYGONS; String path = "ows?service=WFS&version=2.0.0&request=DescribeFeatureType&" + "typeName="+ typeName.getLocalPart() + "&namespace=xmlns(" + URLEncoder.encode(typeName.getNamespaceURI(), "UTF-8") + ")"; Document doc = getAsDOM(path); assertSchema(doc, CiteTestData.POLYGONS); } @Test public void testMissingNameNamespacePrefix() throws Exception { final QName typeName = CiteTestData.POLYGONS; String path = "ows?service=WFS&version=2.0.0&request=DescribeFeatureType&typeName=" + typeName.getLocalPart(); Document doc = getAsDOM(path); assertSchema(doc, CiteTestData.POLYGONS); } /** * Under cite compliance mode, even if the requested typeName is not qualified and it does exist * in the GeoServer's default namespace, the lookup should fail, since the request does not * addresses the typeName either by qualifying it as declared in the getcaps document, or * providing an alternate prefix with its corresponding prefix to namespace mapping. * */ @Test public void testCiteCompliance() throws Exception { final QName typeName = CiteTestData.STREAMS; // make sure typeName _is_ in the default namespace Catalog catalog = getCatalog(); NamespaceInfo defaultNs = catalog.getDefaultNamespace(); GeoServer geoServer = getGeoServer(); WFSInfo service = geoServer.getService(WFSInfo.class); try { // make sure typeName _is_ in the default namespace catalog.setDefaultNamespace(catalog.getNamespaceByURI(typeName.getNamespaceURI())); FeatureTypeInfo typeInfo = catalog.getFeatureTypeByName(typeName.getNamespaceURI(), typeName.getLocalPart()); typeInfo.setEnabled(true); catalog.save(typeInfo); DataStoreInfo store = typeInfo.getStore(); store.setEnabled(true); catalog.save(store); // and request typeName without prefix String path = "ows?service=WFS&version=2.0.0&request=DescribeFeatureType&typeName=" + typeName.getLocalPart(); Document doc; //first, non cite compliant mode should find the type even if namespace is not specified service.setCiteCompliant(false); geoServer.save(service); doc = getAsDOM(path); print(doc); assertSchema(doc, typeName); //then, in cite compliance more, it should not find the type name service.setCiteCompliant(true); geoServer.save(service); doc = getAsDOM(path); //print(doc); assertEquals("ows:ExceptionReport", doc.getDocumentElement().getNodeName()); } finally { catalog.setDefaultNamespace(defaultNs); service.setCiteCompliant(false); geoServer.save(service); } } /** * See https://osgeo-org.atlassian.net/browse/GEOS-3306 * */ @Test public void testPrefixedGetStrictCite() throws Exception { GeoServer geoServer = getGeoServer(); WFSInfo service = geoServer.getService(WFSInfo.class); try { service.setCiteCompliant(true); geoServer.save(service); final QName typeName = CiteTestData.POLYGONS; String path = "ows?service=WFS&version=2.0.0&request=DescribeFeatureType&typeName=" + getLayerId(typeName); Document doc = getAsDOM(path); assertSchema(doc, CiteTestData.POLYGONS); } finally { service.setCiteCompliant(false); geoServer.save(service); } } @Test public void testGML32OutputFormat() throws Exception { Document dom = getAsDOM("ows?service=WFS&version=2.0.0&request=DescribeFeatureType" + "&outputFormat=text/xml;+subtype%3Dgml/3.2&typename=" + getLayerId(CiteTestData.POLYGONS)); assertSchema(dom, CiteTestData.POLYGONS); } @Test public void testGMLAttributeMapping() throws Exception { WFSInfo wfs = getWFS(); GMLInfo gml = wfs.getGML().get(WFSInfo.Version.V_11); gml.setOverrideGMLAttributes(false); getGeoServer().save(wfs); Document dom = getAsDOM("ows?service=WFS&version=2.0.0&request=DescribeFeatureType" + "&typename=" + getLayerId(CiteTestData.PRIMITIVEGEOFEATURE)); assertSchema(dom, CiteTestData.PRIMITIVEGEOFEATURE); XMLAssert.assertXpathNotExists("//xsd:element[@name = 'name']", dom); XMLAssert.assertXpathNotExists("//xsd:element[@name = 'description']", dom); gml.setOverrideGMLAttributes(true); dom = getAsDOM("ows?service=WFS&version=2.0.0&request=DescribeFeatureType" + "&typename=" + getLayerId(CiteTestData.PRIMITIVEGEOFEATURE)); assertSchema(dom, CiteTestData.PRIMITIVEGEOFEATURE); XMLAssert.assertXpathExists("//xsd:element[@name = 'name']", dom); XMLAssert.assertXpathExists("//xsd:element[@name = 'description']", dom); } @Test public void testSOAP() throws Exception { String xml = "<soap:Envelope xmlns:soap='http://www.w3.org/2003/05/soap-envelope'> " + " <soap:Header/> " + " <soap:Body>" + "<wfs:DescribeFeatureType service='WFS' version='2.0.0' " + "xmlns:wfs='http://www.opengis.net/wfs/2.0' " + "xmlns:sf='" + CiteTestData.PRIMITIVEGEOFEATURE.getNamespaceURI() + "'>" + " <wfs:TypeName>" + getLayerId(CiteTestData.PRIMITIVEGEOFEATURE) + "</wfs:TypeName>" + "</wfs:DescribeFeatureType>" + " </soap:Body> " + "</soap:Envelope> "; MockHttpServletResponse resp = postAsServletResponse("wfs", xml, "application/soap+xml"); assertEquals("application/soap+xml", resp.getContentType()); Document dom = dom(new ByteArrayInputStream(resp.getContentAsString().getBytes())); assertEquals("soap:Envelope", dom.getDocumentElement().getNodeName()); print(dom); XMLAssert.assertXpathEvaluatesTo("xsd:base64", "//soap:Body/@type", dom); assertEquals(1, dom.getElementsByTagName("wfs:DescribeFeatureTypeResponse").getLength()); String base64 = dom.getElementsByTagName("wfs:DescribeFeatureTypeResponse").item(0) .getFirstChild().getNodeValue(); byte[] decoded = Base64.decodeBase64(base64.getBytes()); dom = dom(new ByteArrayInputStream(decoded)); assertEquals("xsd:schema", dom.getDocumentElement().getNodeName()); } }