/* (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.wms.wms_1_3; import static org.custommonkey.xmlunit.XMLAssert.assertXpathEvaluatesTo; import static org.custommonkey.xmlunit.XMLAssert.assertXpathExists; import static org.custommonkey.xmlunit.XMLAssert.assertXpathNotExists; import static org.custommonkey.xmlunit.XMLUnit.newXpathEngine; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.List; import java.util.Map; import org.custommonkey.xmlunit.XMLAssert; import org.custommonkey.xmlunit.XMLUnit; import org.custommonkey.xmlunit.XpathEngine; import org.geoserver.catalog.AttributionInfo; import org.geoserver.catalog.CascadeDeleteVisitor; import org.geoserver.catalog.Catalog; import org.geoserver.catalog.CatalogBuilder; import org.geoserver.catalog.DataLinkInfo; import org.geoserver.catalog.DataStoreInfo; import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.catalog.Keyword; import org.geoserver.catalog.LayerGroupHelper; import org.geoserver.catalog.LayerGroupInfo; import org.geoserver.catalog.LayerInfo; import org.geoserver.catalog.MetadataLinkInfo; import org.geoserver.catalog.PublishedInfo; import org.geoserver.catalog.ResourceInfo; import org.geoserver.catalog.StyleInfo; import org.geoserver.catalog.WorkspaceInfo; import org.geoserver.catalog.impl.AbstractDecorator; import org.geoserver.catalog.impl.DataStoreInfoImpl; import org.geoserver.catalog.impl.FeatureTypeInfoImpl; import org.geoserver.catalog.impl.LayerInfoImpl; import org.geoserver.catalog.impl.ModificationProxy; import org.geoserver.catalog.impl.NamespaceInfoImpl; import org.geoserver.catalog.impl.WorkspaceInfoImpl; import org.geoserver.config.ContactInfo; import org.geoserver.config.GeoServerInfo; import org.geoserver.data.test.MockData; import org.geoserver.data.test.SystemTestData; import org.geoserver.ows.util.OwsUtils; import org.geoserver.wfs.json.JSONType; import org.geoserver.wms.WMSInfo; import org.geoserver.wms.WMSTestSupport; import org.geoserver.wms.map.OpenLayersMapOutputFormat; import org.geotools.feature.NameImpl; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.referencing.CRS; import org.hamcrest.CoreMatchers; import org.junit.Test; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import java.lang.reflect.Proxy; import java.util.UUID; import java.util.stream.Collectors; /** * WMS 1.3 GetCapabilities integration tests * * <p> * These tests are initialy ported from the 1.1.1 capabilities integration tests * </p> * * @author Gabriel Roldan * */ public class CapabilitiesIntegrationTest extends WMSTestSupport { public CapabilitiesIntegrationTest() { super(); } @Override protected void setUpTestData(SystemTestData testData) throws Exception { super.setUpTestData(testData); testData.setUpDefaultRasterLayers(); } @Override protected void onSetUp(SystemTestData testData) throws Exception { super.onSetUp(testData); Catalog catalog = getCatalog(); DataStoreInfo info =catalog.getDataStoreByName(MockData.SF_PREFIX); info.setEnabled(false); catalog.save(info); GeoServerInfo global = getGeoServer().getGlobal(); global.getSettings().setProxyBaseUrl("src/test/resources/geoserver"); getGeoServer().save(global); // add a workspace qualified style WorkspaceInfo ws = catalog.getWorkspaceByName(MockData.CITE_PREFIX); testData.addStyle(ws, "Lakes", "Lakes.sld", SystemTestData.class, catalog); StyleInfo lakesStyle = catalog.getStyleByName(ws, "Lakes"); LayerInfo lakesLayer = catalog.getLayerByName(MockData.LAKES.getLocalPart()); lakesLayer.setDefaultStyle(lakesStyle); catalog.save(lakesLayer); setupOpaqueGroup(catalog); } @Override protected void registerNamespaces(Map<String, String> namespaces) { namespaces.put("wms", "http://www.opengis.net/wms"); namespaces.put("ows", "http://www.opengis.net/ows"); } @org.junit.Test public void testCapabilities() throws Exception { Document dom = dom(get("wms?request=getCapabilities&version=1.3.0"), false); Element e = dom.getDocumentElement(); assertEquals("WMS_Capabilities", e.getLocalName()); } @org.junit.Test public void testCapabilitiesNoWGS84DD() throws Exception { Document dom = dom(get("wms?request=getCapabilities&version=1.3.0"), false); // print(dom); XMLAssert.assertXpathNotExists("//wms:CRS[text() = 'EPSG:WGS84(DD)']", dom); } @org.junit.Test public void testGetCapsContainsNoDisabledTypes() throws Exception { Document doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); // print(doc); assertEquals("WMS_Capabilities", doc.getDocumentElement().getNodeName()); // see that disabled elements are disabled for good assertXpathEvaluatesTo("0", "count(//Name[text()='sf:PrimitiveGeoFeature'])", doc); } @org.junit.Test public void testFilteredCapabilitiesCite() throws Exception { Document dom = dom(get("wms?request=getCapabilities&version=1.3.0&namespace=cite"), true); Element e = dom.getDocumentElement(); assertEquals("WMS_Capabilities", e.getLocalName()); XpathEngine xpath = XMLUnit.newXpathEngine(); assertTrue(xpath.getMatchingNodes("//wms:Layer/wms:Name[starts-with(., cite)]", dom) .getLength() > 0); assertEquals(0, xpath.getMatchingNodes("//wms:Layer/wms:Name[not(starts-with(., cite))]", dom) .getLength()); } @org.junit.Test public void testLayerCount() throws Exception { int expectedLayerCount = getRawTopLayerCount(); Document dom = dom(get("wms?request=GetCapabilities&version=1.3.0"), true); // print(dom); XpathEngine xpath = XMLUnit.newXpathEngine(); NodeList nodeLayers = xpath.getMatchingNodes( "/wms:WMS_Capabilities/wms:Capability/wms:Layer/wms:Layer", dom); assertEquals(expectedLayerCount /* the layers under the opaque group */, nodeLayers.getLength()); } @org.junit.Test public void testWorkspaceQualified() throws Exception { Document dom = dom(get("cite/wms?request=getCapabilities&version=1.3.0"), true); Element e = dom.getDocumentElement(); assertEquals("WMS_Capabilities", e.getLocalName()); XpathEngine xpath = XMLUnit.newXpathEngine(); assertTrue(xpath.getMatchingNodes("//wms:Layer/wms:Name[starts-with(., cite)]", dom) .getLength() > 0); assertEquals(0, xpath.getMatchingNodes("//wms:Layer/wms:Name[not(starts-with(., cite))]", dom) .getLength()); NodeList nodes = xpath.getMatchingNodes("//wms:Layer//wms:OnlineResource", dom); assertTrue(nodes.getLength() > 0); for (int i = 0; i < nodes.getLength(); i++) { e = (Element) nodes.item(i); String attribute = e.getAttribute("xlink:href"); assertTrue(attribute.contains("geoserver/cite/ows")); } } @org.junit.Test public void testLayerQualified() throws Exception { // Qualify the request with a layer. Other layers should not be included. Document dom = dom(get("cite/Forests/wms?service=WMS&request=getCapabilities&version=1.3.0"), true); // print(dom); Element e = dom.getDocumentElement(); assertEquals("WMS_Capabilities", e.getLocalName()); XpathEngine xpath = XMLUnit.newXpathEngine(); assertEquals(0, xpath .getMatchingNodes("//wms:Layer/wms:Name[starts-with(., 'cite:Forests')]", dom) .getLength()); assertEquals(1, xpath.getMatchingNodes("//wms:Layer/wms:Layer", dom).getLength()); NodeList nodes = xpath.getMatchingNodes("//wms:Layer//wms:OnlineResource", dom); assertTrue(nodes.getLength() > 0); for (int i = 0; i < nodes.getLength(); i++) { e = (Element) nodes.item(i); String attribute = e.getAttribute("xlink:href"); assertTrue(attribute.contains("geoserver/cite/Forests/ows")); } } @org.junit.Test public void testAttribution() throws Exception { // Uncomment the following lines if you want to use DTD validation for these tests // (by passing false as the second param to getAsDOM()) // BUG: Currently, this doesn't seem to actually validate the document, although // 'validation' fails if the DTD is missing // GeoServerInfo global = getGeoServer().getGlobal(); // global.setProxyBaseUrl("src/test/resources/geoserver"); // getGeoServer().save(global); Document doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); assertXpathEvaluatesTo("0", "count(//wms:Attribution)", doc); // Add attribution to one of the layers LayerInfo points = getCatalog().getLayerByName(MockData.POINTS.getLocalPart()); AttributionInfo attr = points.getAttribution(); attr.setTitle("Point Provider"); getCatalog().save(points); doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); assertXpathEvaluatesTo("1", "count(//wms:Attribution)", doc); assertXpathEvaluatesTo("1", "count(//wms:Attribution/wms:Title)", doc); // Add href to same layer attr = points.getAttribution(); attr.setHref("http://example.com/points/provider"); getCatalog().save(points); doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); // print(doc); assertXpathEvaluatesTo("1", "count(//wms:Attribution)", doc); assertXpathEvaluatesTo("1", "count(//wms:Attribution/wms:Title)", doc); assertXpathEvaluatesTo("1", "count(//wms:Attribution/wms:OnlineResource)", doc); // Add logo to same layer attr = points.getAttribution(); attr.setLogoURL("http://example.com/points/logo"); attr.setLogoType("image/logo"); attr.setLogoHeight(50); attr.setLogoWidth(50); getCatalog().save(points); doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); // print(doc); assertXpathEvaluatesTo("1", "count(//wms:Attribution)", doc); assertXpathEvaluatesTo("1", "count(//wms:Attribution/wms:Title)", doc); assertXpathEvaluatesTo("1", "count(//wms:Attribution/wms:LogoURL)", doc); } @Test public void testLayerGroup() throws Exception { LayerInfo points = getCatalog().getLayerByName(MockData.POINTS.getLocalPart()); CatalogBuilder builder = new CatalogBuilder(getCatalog()); //create layergr LayerGroupInfo lg = getCatalog().getFactory().createLayerGroup(); //attribution lg.setName("MyLayerGroup"); lg.getLayers().add(points); builder.calculateLayerGroupBounds(lg, CRS.decode("EPSG:4326")); lg.setAttribution(getCatalog().getFactory().createAttribution()); lg.getAttribution().setTitle("My Attribution"); MetadataLinkInfo info = getCatalog().getFactory().createMetadataLink(); info.setType("text/html"); info.setMetadataType("FGDC"); info.setContent("http://my/metadata/link"); lg.getMetadataLinks().add(info); getCatalog().add(lg); try { Document doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.1.1", true); //print(doc); assertXpathEvaluatesTo("1", "count(//Layer[Name='MyLayerGroup']/Attribution)", doc); assertXpathEvaluatesTo("My Attribution", "//Layer[Name='MyLayerGroup']/Attribution/Title", doc); assertXpathEvaluatesTo("1", "count(//Layer[Name='MyLayerGroup']/MetadataURL)", doc); assertXpathEvaluatesTo("http://my/metadata/link", "//Layer[Name='MyLayerGroup']/MetadataURL/OnlineResource/@xlink:href", doc); } finally { //clean up getCatalog().remove(lg); } } @org.junit.Test public void testAlternateStyles() throws Exception { // add an alternate style to Fifteen StyleInfo pointStyle = getCatalog().getStyleByName("point"); LayerInfo layer = getCatalog().getLayerByName("Fifteen"); layer.getStyles().add(pointStyle); getCatalog().save(layer); Document doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); // print(doc); assertXpathEvaluatesTo("1", "count(//wms:Layer[wms:Name='cdf:Fifteen'])", doc); assertXpathEvaluatesTo("2", "count(//wms:Layer[wms:Name='cdf:Fifteen']/wms:Style)", doc); XpathEngine xpath = newXpathEngine(); String href = xpath .evaluate( "//wms:Layer[wms:Name='cdf:Fifteen']/wms:Style[wms:Name='Default']/wms:LegendURL/wms:OnlineResource/@xlink:href", doc); assertTrue(href.contains("GetLegendGraphic")); assertTrue(href.contains("layer=cdf%3AFifteen")); assertFalse(href.contains("style")); href = xpath .evaluate( "//wms:Layer[wms:Name='cdf:Fifteen']/wms:Style[wms:Name='point']/wms:LegendURL/wms:OnlineResource/@xlink:href", doc); assertTrue(href.contains("GetLegendGraphic")); assertTrue(href.contains("layer=cdf%3AFifteen")); assertTrue(href.contains("style=point")); } @org.junit.Test public void testServiceMetadata() throws Exception { final WMSInfo service = getGeoServer().getService(WMSInfo.class); service.setTitle("test title"); service.setAbstract("test abstract"); service.setAccessConstraints("test accessConstraints"); service.setFees("test fees"); service.getKeywords().clear(); service.getKeywords().add(new Keyword("test keyword 1")); service.getKeywords().add(new Keyword("test keyword 2")); service.setMaintainer("test maintainer"); service.setOnlineResource("http://example.com/geoserver"); GeoServerInfo global = getGeoServer().getGlobal(); ContactInfo contact = global.getContact(); contact.setAddress("__address"); contact.setAddressCity("__city"); contact.setAddressCountry("__country"); contact.setAddressPostalCode("__ZIP"); contact.setAddressState("__state"); contact.setAddressType("__type"); contact.setContactEmail("e@mail"); contact.setContactOrganization("__org"); contact.setContactFacsimile("__fax"); contact.setContactPerson("__me"); contact.setContactPosition("__position"); contact.setContactVoice("__phone"); getGeoServer().save(global); getGeoServer().save(service); Document doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); //print(doc); String base = "wms:WMS_Capabilities/wms:Service/"; assertXpathEvaluatesTo("WMS", base + "wms:Name", doc); assertXpathEvaluatesTo("test title", base + "wms:Title", doc); assertXpathEvaluatesTo("test abstract", base + "wms:Abstract", doc); assertXpathEvaluatesTo("test keyword 1", base + "wms:KeywordList/wms:Keyword[1]", doc); assertXpathEvaluatesTo("test keyword 2", base + "wms:KeywordList/wms:Keyword[2]", doc); assertXpathEvaluatesTo("http://example.com/geoserver", base + "wms:OnlineResource/@xlink:href", doc); String cinfo = base + "wms:ContactInformation/"; assertXpathEvaluatesTo("__me", cinfo + "wms:ContactPersonPrimary/wms:ContactPerson", doc); assertXpathEvaluatesTo("__org", cinfo + "wms:ContactPersonPrimary/wms:ContactOrganization", doc); assertXpathEvaluatesTo("__position", cinfo + "wms:ContactPosition", doc); assertXpathEvaluatesTo("__type", cinfo + "wms:ContactAddress/wms:AddressType", doc); assertXpathEvaluatesTo("__address", cinfo + "wms:ContactAddress/wms:Address", doc); assertXpathEvaluatesTo("__city", cinfo + "wms:ContactAddress/wms:City", doc); assertXpathEvaluatesTo("__state", cinfo + "wms:ContactAddress/wms:StateOrProvince", doc); assertXpathEvaluatesTo("__ZIP", cinfo + "wms:ContactAddress/wms:PostCode", doc); assertXpathEvaluatesTo("__country", cinfo + "wms:ContactAddress/wms:Country", doc); assertXpathEvaluatesTo("__phone", cinfo + "wms:ContactVoiceTelephone", doc); assertXpathEvaluatesTo("__fax", cinfo + "wms:ContactFacsimileTelephone", doc); assertXpathEvaluatesTo("e@mail", cinfo + "wms:ContactElectronicMailAddress", doc); } @Test public void testNoFeesOrContraints() throws Exception { final WMSInfo service = getGeoServer().getService(WMSInfo.class); service.setAccessConstraints(null); service.setFees(null); getGeoServer().save(service); Document doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); // print(doc); String base = "wms:WMS_Capabilities/wms:Service/"; assertXpathEvaluatesTo("WMS", base + "wms:Name", doc); assertXpathEvaluatesTo("none", base + "wms:Fees", doc); assertXpathEvaluatesTo("none", base + "wms:AccessConstraints", doc); } @Test public void testExceptions() throws Exception { Document doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); assertXpathEvaluatesTo("XML", "wms:WMS_Capabilities/wms:Capability/wms:Exception/wms:Format[1]", doc); assertXpathEvaluatesTo("INIMAGE", "wms:WMS_Capabilities/wms:Capability/wms:Exception/wms:Format[2]", doc); assertXpathEvaluatesTo("BLANK", "wms:WMS_Capabilities/wms:Capability/wms:Exception/wms:Format[3]", doc); assertXpathEvaluatesTo("JSON", "wms:WMS_Capabilities/wms:Capability/wms:Exception/wms:Format[4]", doc); boolean jsonpOriginal = JSONType.isJsonpEnabled(); try { JSONType.setJsonpEnabled(true); doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); assertXpathEvaluatesTo("JSONP", "wms:WMS_Capabilities/wms:Capability/wms:Exception/wms:Format[5]", doc); assertXpathEvaluatesTo("5", "count(wms:WMS_Capabilities/wms:Capability/wms:Exception/wms:Format)", doc); JSONType.setJsonpEnabled(false); doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); assertXpathEvaluatesTo("4", "count(wms:WMS_Capabilities/wms:Capability/wms:Exception/wms:Format)", doc); } finally { JSONType.setJsonpEnabled(jsonpOriginal); } } @org.junit.Test public void testQueryable() throws Exception { LayerInfo lines = getCatalog().getLayerByName(MockData.LINES.getLocalPart()); lines.setQueryable(true); getCatalog().save(lines); LayerInfo points = getCatalog().getLayerByName(MockData.POINTS.getLocalPart()); points.setQueryable(false); getCatalog().save(points); String linesName = MockData.LINES.getPrefix() + ":" + MockData.LINES.getLocalPart(); String pointsName = MockData.POINTS.getPrefix() + ":" + MockData.POINTS.getLocalPart(); Document doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); assertXpathEvaluatesTo("1", "//wms:Layer[wms:Name='" + linesName + "']/@queryable", doc); assertXpathEvaluatesTo("0", "//wms:Layer[wms:Name='" + pointsName + "']/@queryable", doc); } @org.junit.Test public void testOpaque() throws Exception { LayerInfo lines = getCatalog().getLayerByName(MockData.LINES.getLocalPart()); lines.setOpaque(true); getCatalog().save(lines); LayerInfo points = getCatalog().getLayerByName(MockData.POINTS.getLocalPart()); points.setOpaque(false); getCatalog().save(points); String linesName = MockData.LINES.getPrefix() + ":" + MockData.LINES.getLocalPart(); String pointsName = MockData.POINTS.getPrefix() + ":" + MockData.POINTS.getLocalPart(); Document doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); assertXpathEvaluatesTo("1", "//wms:Layer[wms:Name='" + linesName + "']/@opaque", doc); assertXpathEvaluatesTo("0", "//wms:Layer[wms:Name='" + pointsName + "']/@opaque", doc); } @org.junit.Test public void testKeywordVocab() throws Exception { FeatureTypeInfo lines = getFeatureTypeInfo(MockData.LINES); Keyword kw = new Keyword("foo"); kw.setVocabulary("bar"); lines.getKeywords().add(kw); getCatalog().save(lines); WMSInfo wms = getGeoServer().getService(WMSInfo.class); kw = new Keyword("baz"); kw.setVocabulary("bar"); wms.getKeywords().add(kw); getGeoServer().save(wms); String linesName = MockData.LINES.getPrefix() + ":" + MockData.LINES.getLocalPart(); Document doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); String xpath = "//wms:Layer[wms:Name='" + linesName + "']/wms:KeywordList/wms:Keyword[@vocabulary='bar']"; assertXpathExists(xpath, doc); assertXpathEvaluatesTo("foo", xpath, doc); xpath = "//wms:Service/wms:KeywordList/wms:Keyword[@vocabulary='bar']"; assertXpathExists(xpath, doc); assertXpathEvaluatesTo("baz", xpath, doc); } @org.junit.Test public void testBoundingBoxCRS84() throws Exception { Document doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); assertXpathExists("/wms:WMS_Capabilities/wms:Capability/wms:Layer/wms:BoundingBox[@CRS = 'CRS:84']", doc); assertXpathExists("/wms:WMS_Capabilities/wms:Capability/wms:Layer//wms:Layer/wms:BoundingBox[@CRS = 'CRS:84']", doc); } @org.junit.Test public void testMetadataLinks() throws Exception { String layerName = MockData.POINTS.getPrefix() + ":" + MockData.POINTS.getLocalPart(); LayerInfo layer = getCatalog().getLayerByName(MockData.POINTS.getLocalPart()); MetadataLinkInfo mdlink = getCatalog().getFactory().createMetadataLink(); mdlink.setMetadataType("FGDC"); mdlink.setContent("http://geoserver.org"); mdlink.setType("text/xml"); ResourceInfo resource = layer.getResource(); resource.getMetadataLinks().add(mdlink); getCatalog().save(resource); Document doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); String xpath = "//wms:Layer[wms:Name='" + layerName + "']/wms:MetadataURL/wms:Format"; assertXpathEvaluatesTo("text/xml", xpath, doc); xpath = "//wms:Layer[wms:Name='" + layerName + "']/wms:MetadataURL/@type"; assertXpathEvaluatesTo("FGDC", xpath, doc); xpath = "//wms:Layer[wms:Name='" + layerName + "']/wms:MetadataURL/wms:OnlineResource/@xlink:type"; assertXpathEvaluatesTo("simple", xpath, doc); xpath = "//wms:Layer[wms:Name='" + layerName + "']/wms:MetadataURL/wms:OnlineResource/@xlink:href"; assertXpathEvaluatesTo("http://geoserver.org", xpath, doc); // Test transforming localhost to proxyBaseUrl GeoServerInfo global = getGeoServer().getGlobal(); String proxyBaseUrl = global.getSettings().getProxyBaseUrl(); mdlink.setContent("/metadata"); getCatalog().save(resource); doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); assertXpathEvaluatesTo(proxyBaseUrl + "/metadata", xpath, doc); // Test KVP in URL String query = "key=value"; mdlink.setContent("/metadata?" + query); getCatalog().save(resource); doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); assertXpathEvaluatesTo(proxyBaseUrl + "/metadata?" + query, xpath, doc); mdlink.setContent("http://localhost/metadata?" + query); getCatalog().save(resource); doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); assertXpathEvaluatesTo("http://localhost/metadata?" + query, xpath, doc); } @org.junit.Test public void testDataLinks() throws Exception { String layerName = MockData.POINTS.getPrefix() + ":" + MockData.POINTS.getLocalPart(); LayerInfo layer = getCatalog().getLayerByName(MockData.POINTS.getLocalPart()); DataLinkInfo mdlink = getCatalog().getFactory().createDataLink(); mdlink.setContent("http://geoserver.org"); mdlink.setType("text/xml"); ResourceInfo resource = layer.getResource(); resource.getDataLinks().add(mdlink); getCatalog().save(resource); Document doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); String xpath = "//wms:Layer[wms:Name='" + layerName + "']/wms:DataURL/wms:Format"; assertXpathEvaluatesTo("text/xml", xpath, doc); xpath = "//wms:Layer[wms:Name='" + layerName + "']/wms:DataURL/wms:OnlineResource/@xlink:type"; assertXpathEvaluatesTo("simple", xpath, doc); xpath = "//wms:Layer[wms:Name='" + layerName + "']/wms:DataURL/wms:OnlineResource/@xlink:href"; assertXpathEvaluatesTo("http://geoserver.org", xpath, doc); // Test transforming localhost to proxyBaseUrl GeoServerInfo global = getGeoServer().getGlobal(); String proxyBaseUrl = global.getSettings().getProxyBaseUrl(); mdlink.setContent("/metadata"); getCatalog().save(resource); doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); assertXpathEvaluatesTo(proxyBaseUrl + "/metadata", xpath, doc); // Test KVP in URL String query = "key=value"; mdlink.setContent("/metadata?" + query); getCatalog().save(resource); doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); assertXpathEvaluatesTo(proxyBaseUrl + "/metadata?" + query, xpath, doc); mdlink.setContent("http://localhost/metadata?" + query); getCatalog().save(resource); doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); assertXpathEvaluatesTo("http://localhost/metadata?" + query, xpath, doc); } // [GEOS-6312] OpenLayers output format is not listed in WMS 1.3 capabilities document @Test public void testOpenlayersFormat() throws Exception { Document doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); assertXpathEvaluatesTo("1", "count(//wms:GetMap[wms:Format = '" + OpenLayersMapOutputFormat.MIME_TYPE + "'])", doc); } @Test public void testStyleWorkspaceQualified() throws Exception { Document doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); // check the style name got prefixed too assertXpathEvaluatesTo("cite:Lakes", "//wms:Layer[wms:Name='cite:Lakes']/wms:Style[1]/wms:Name", doc); } @Test public void testDuplicateLayerGroup() throws Exception { // see https://osgeo-org.atlassian.net/browse/GEOS-6154 Catalog catalog = getCatalog(); LayerInfo lakes = catalog.getLayerByName(getLayerId(MockData.LAKES)); lakes.setAdvertised(false); catalog.save(lakes); try { Document doc = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); // print(doc); // should show up just once assertXpathEvaluatesTo("1", "count(//wms:Layer[wms:Name='nature'])", doc); assertXpathEvaluatesTo("1", "count(//wms:Layer[wms:Title='containerGroup']/wms:Layer[wms:Name='nature'])", doc); } finally { lakes.setAdvertised(true); catalog.save(lakes); } } @Test public void testOpaqueGroup() throws Exception { Document dom = dom(get("wms?request=GetCapabilities&version=1.3.0"), true); // the layer group is there, but not the contained layers assertXpathEvaluatesTo("1", "count(//wms:Layer[wms:Name='opaqueGroup'])", dom); for (LayerInfo l : getCatalog().getLayerGroupByName(OPAQUE_GROUP).layers()) { assertXpathNotExists("//wms:Layer[wms:Name='" + l.prefixedName() + "']", dom); }; } @Test public void testNestedGroupInOpaqueGroup() throws Exception { Catalog catalog = getCatalog(); // nest container inside opaque, this should make it disappear from the caps LayerGroupInfo container = catalog.getLayerGroupByName(CONTAINER_GROUP); LayerGroupInfo opaque = catalog.getLayerGroupByName(OPAQUE_GROUP); opaque.getLayers().add(container); opaque.getStyles().add(null); catalog.save(opaque); try { Document dom = getAsDOM("wms?request=GetCapabilities&version=1.3.0"); // print(dom); // the layer group is there, but not the contained layers, which are not visible anymore assertXpathEvaluatesTo("1", "count(//wms:Layer[wms:Name='opaqueGroup'])", dom); for (PublishedInfo p : getCatalog().getLayerGroupByName(OPAQUE_GROUP).getLayers()) { assertXpathNotExists("//wms:Layer[wms:Name='" + p.prefixedName() + "']", dom); } ; // now check the layer count too, we just hid everything in the container layer List<LayerInfo> nestedLayers = new LayerGroupHelper(container).allLayers(); // System.out.println(nestedLayers); int expectedLayerCount = getRawTopLayerCount() // layers gone due to the nesting - nestedLayers.size() /* container has been nested and disappeared */ - 1; XpathEngine xpath = XMLUnit.newXpathEngine(); NodeList nodeLayers = xpath.getMatchingNodes( "/wms:WMS_Capabilities/wms:Capability/wms:Layer/wms:Layer", dom); assertEquals(expectedLayerCount /* the layers under the opaque group */, nodeLayers.getLength()); } finally { // restore the configuration opaque.getLayers().remove(container); opaque.getStyles().remove(opaque.getStyles().size() - 1); catalog.save(opaque); } } @Test public void testGlobalBoundingBoxForLayerGroups() throws Exception { Catalog catalog = getCatalog(); // create a new workspace for this tests WorkspaceInfoImpl workspace = new WorkspaceInfoImpl(); workspace.setName("NON_ADVERTISED"); NamespaceInfoImpl nameSpace = new NamespaceInfoImpl(); nameSpace.setPrefix("NON_ADVERTISED"); nameSpace.setURI("http://non-advertised.org"); // remove all layer groups and store them List<LayerGroupInfo> layerGroups = catalog.getLayerGroups().stream() .map(this::unwrapLayerGroup).collect(Collectors.toList()); catalog.getLayerGroups().forEach(catalog::remove); try { catalog.add(workspace); catalog.add(nameSpace); // add some layers by duplicating existing layers and create a layer group LayerInfo layer1 = cloneVectorLayerIntoWorkspace(workspace, nameSpace, MockData.BUILDINGS.getLocalPart()); LayerInfo layer2 = cloneVectorLayerIntoWorkspace(workspace, nameSpace, MockData.BRIDGES.getLocalPart()); LayerGroupInfo layerGroup = createLayerGroup(workspace, "NON_ADVERTISED", layer1, layer2); // reduce layer group bounds and store the original bounds which correspond to all layers bounds ReferencedEnvelope layersBounds = layerGroup.getBounds(); ReferencedEnvelope layerGroupBounds = new ReferencedEnvelope(-10, 10, -20, 20, layersBounds.getCoordinateReferenceSystem()); layerGroup.setBounds(layerGroupBounds); catalog.save(layerGroup); // perform a get capabilities request targeting only the created workspace Document document = getAsDOM("NON_ADVERTISED/wms?service=WMS&request=getCapabilities&version=1.3.0", true); checkGlobalBoundingBox(layersBounds, document); // make layers non advertised layer1.setAdvertised(false); layer2.setAdvertised(false); catalog.save(layer1); catalog.save(layer2); // perform a get capabilities request targeting only the created workspace document = getAsDOM("NON_ADVERTISED/wms?service=WMS&request=getCapabilities&version=1.3.0", true); checkGlobalBoundingBox(layerGroupBounds, document); } finally { // add layer groups back layerGroups.forEach(catalog::add); // remove the created workspace and namespace CascadeDeleteVisitor deleteVisitor = new CascadeDeleteVisitor(catalog); deleteVisitor.visit(workspace); catalog.remove(nameSpace); catalog.remove(workspace); } } @Test public void testLayerGroupsPerWorkspace() throws Exception { Catalog catalog = getCatalog(); // create workspace A WorkspaceInfoImpl workspaceA = new WorkspaceInfoImpl(); workspaceA.setName("LG_TEST_WORKSPACE_A"); NamespaceInfoImpl nameSpaceA = new NamespaceInfoImpl(); nameSpaceA.setPrefix("LG_TEST_WORKSPACE_A"); nameSpaceA.setURI("http://lg-test-workspace-a.org"); // create workspace B WorkspaceInfoImpl workspaceB = new WorkspaceInfoImpl(); workspaceB.setName("LG_TEST_WORKSPACE_B"); NamespaceInfoImpl nameSpaceB = new NamespaceInfoImpl(); nameSpaceB.setPrefix("LG_TEST_WORKSPACE_B"); nameSpaceB.setURI("http://lg-test-workspace-b.org"); // keep global layers group reference LayerGroupInfo globalLayerGroup = null; try { // save workspace A catalog.add(workspaceA); catalog.add(nameSpaceA); // save workspace B catalog.add(workspaceB); catalog.add(nameSpaceB); // create layers groups createLayerGroup(workspaceA, "LAYER_GROUP_A", cloneVectorLayerIntoWorkspace(workspaceA, nameSpaceA, MockData.BUILDINGS.getLocalPart()), cloneVectorLayerIntoWorkspace(workspaceA, nameSpaceA, MockData.BRIDGES.getLocalPart())); createLayerGroup(workspaceB, "LAYER_GROUP_B", cloneVectorLayerIntoWorkspace(workspaceB, nameSpaceB, MockData.BUILDINGS.getLocalPart()), cloneVectorLayerIntoWorkspace(workspaceB, nameSpaceB, MockData.BRIDGES.getLocalPart())); globalLayerGroup = createLayerGroup("LAYER_GROUP_C", catalog.getLayerByName(MockData.BUILDINGS.getLocalPart()), catalog.getLayerByName(MockData.BRIDGES.getLocalPart())); // perform a get capabilities request targeting the global service Document document = getAsDOM("wms?service=WMS&request=getCapabilities&version=1.3.0", true); assertXpathEvaluatesTo("1", "count(//wms:Capability/wms:Layer/wms:Layer[wms:Name='LG_TEST_WORKSPACE_A:LAYER_GROUP_A'])", document); assertXpathEvaluatesTo("1", "count(//wms:Capability/wms:Layer/wms:Layer[wms:Name='LG_TEST_WORKSPACE_B:LAYER_GROUP_B'])", document); assertXpathEvaluatesTo("0", "count(//wms:Capability/wms:Layer/wms:Layer[wms:Name='LAYER_GROUP_A'])", document); assertXpathEvaluatesTo("0", "count(//wms:Capability/wms:Layer/wms:Layer[wms:Name='LAYER_GROUP_B'])", document); assertXpathEvaluatesTo("1", "count(//wms:Capability/wms:Layer/wms:Layer[wms:Name='LAYER_GROUP_C'])", document); // perform a get capabilities request targeting workspace A service document = getAsDOM("LG_TEST_WORKSPACE_A/wms?service=WMS&request=getCapabilities&version=1.3.0", true); assertXpathEvaluatesTo("0", "count(//wms:Capability/wms:Layer/wms:Layer[wms:Name='LG_TEST_WORKSPACE_A:LAYER_GROUP_A'])", document); assertXpathEvaluatesTo("0", "count(//wms:Capability/wms:Layer/wms:Layer[wms:Name='LG_TEST_WORKSPACE_B:LAYER_GROUP_B'])", document); assertXpathEvaluatesTo("1", "count(//wms:Capability/wms:Layer/wms:Layer[wms:Name='LAYER_GROUP_A'])", document); assertXpathEvaluatesTo("0", "count(//wms:Capability/wms:Layer/wms:Layer[wms:Name='LAYER_GROUP_B'])", document); assertXpathEvaluatesTo("0", "count(//wms:Capability/wms:Layer/wms:Layer[wms:Name='LAYER_GROUP_C'])", document); // perform a get capabilities request targeting workspace B service document = getAsDOM("LG_TEST_WORKSPACE_B/wms?service=WMS&request=getCapabilities&version=1.3.0", true); assertXpathEvaluatesTo("0", "count(//wms:Capability/wms:Layer/wms:Layer[wms:Name='LG_TEST_WORKSPACE_A:LAYER_GROUP_A'])", document); assertXpathEvaluatesTo("0", "count(//wms:Capability/wms:Layer/wms:Layer[wms:Name='LG_TEST_WORKSPACE_B:LAYER_GROUP_B'])", document); assertXpathEvaluatesTo("0", "count(//wms:Capability/wms:Layer/wms:Layer[wms:Name='LAYER_GROUP_A'])", document); assertXpathEvaluatesTo("1", "count(//wms:Capability/wms:Layer/wms:Layer[wms:Name='LAYER_GROUP_B'])", document); assertXpathEvaluatesTo("0", "count(//wms:Capability/wms:Layer/wms:Layer[wms:Name='LAYER_GROUP_C'])", document); } finally { // remove the created workspaces and namespaces CascadeDeleteVisitor deleteVisitor = new CascadeDeleteVisitor(catalog); deleteVisitor.visit(workspaceA); catalog.remove(nameSpaceA); catalog.remove(workspaceA); deleteVisitor.visit(workspaceB); catalog.remove(nameSpaceB); catalog.remove(workspaceB); // remove global layer group if (globalLayerGroup != null) { catalog.remove(globalLayerGroup); } } } /** * Check that the global bounding box matches the expected envelope */ private void checkGlobalBoundingBox(ReferencedEnvelope expectedBoundingBox, Document capabilitiesResult) throws Exception { // check that the returned capabilities document contains the correct bounding box XpathEngine xpath = newXpathEngine(); // extract bounding box values from the capabilities document String minX = xpath.evaluate("//wms:Capability/wms:Layer/wms:EX_GeographicBoundingBox/wms:westBoundLongitude/text()", capabilitiesResult); String maxX = xpath.evaluate("//wms:Capability/wms:Layer/wms:EX_GeographicBoundingBox/wms:eastBoundLongitude/text()", capabilitiesResult); String minY = xpath.evaluate("//wms:Capability/wms:Layer/wms:EX_GeographicBoundingBox/wms:southBoundLatitude/text()", capabilitiesResult); String maxY = xpath.evaluate("//wms:Capability/wms:Layer/wms:EX_GeographicBoundingBox/wms:northBoundLatitude/text()", capabilitiesResult); // check bounding box values checkNumberSimilar(minX, expectedBoundingBox.getMinX(), 0.0001); checkNumberSimilar(maxX, expectedBoundingBox.getMaxX(), 0.0001); checkNumberSimilar(minY, expectedBoundingBox.getMinY(), 0.0001); checkNumberSimilar(maxY, expectedBoundingBox.getMaxY(), 0.0001); } /** * Check that a number represent by a string is similar to the expected number * A number is considered similar to another if the difference between them is * inferior or equal to the provided precision. */ private void checkNumberSimilar(String rawValue, double expected, double precision) { // try to extract a double value assertThat(rawValue, is(notNullValue())); assertThat(rawValue.trim().isEmpty(), is(false)); double value = 0; try { value = Double.parseDouble(rawValue); } catch (NumberFormatException exception) { fail(String.format("Value '%s' is not a number.", rawValue)); } // compare the parsed double value with the expected one double difference = Math.abs(expected - value); assertThat(difference <= precision, is(true)); } /** * Helper method that unwraps a layer group making him suitable to be added * to the catalog. If proxyfied the proxy will also be removed. */ private LayerGroupInfo unwrapLayerGroup(LayerGroupInfo layerGroup) { // get the original layer group object while (layerGroup instanceof AbstractDecorator) { AbstractDecorator decorator = (AbstractDecorator) layerGroup; layerGroup = (LayerGroupInfo) decorator.unwrap(LayerGroupInfo.class); } // catalog detach doesn't work for layer groups if (Proxy.isProxyClass(layerGroup.getClass())) { // we have a proxy we need to get rid of the proxy ModificationProxy proxy = (ModificationProxy) Proxy.getInvocationHandler(layerGroup); proxy.commit(); layerGroup = (LayerGroupInfo) proxy.getProxyObject(); } return layerGroup; } /** * Helper method that creates a layer group using the provided name and layers. */ private LayerGroupInfo createLayerGroup(String layerGroupName, LayerInfo... layers) throws Exception { return createLayerGroup(null, layerGroupName, layers); } /** * Helper method that creates a layer group using the provided name, workspace and layers. */ private LayerGroupInfo createLayerGroup(WorkspaceInfo workspace, String layerGroupName, LayerInfo... layers) throws Exception { // create a new layer group using the provided name LayerGroupInfo layerGroup = getCatalog().getFactory().createLayerGroup(); layerGroup.setName(layerGroupName); // set workspace layerGroup.setWorkspace(workspace); // add the provided layers for (LayerInfo layerInfo : layers) { layerGroup.getLayers().add(layerInfo); } // set the layer group bounds by merging all layers bounds CatalogBuilder catalogBuilder = new CatalogBuilder(getCatalog()); catalogBuilder.calculateLayerGroupBounds(layerGroup); getCatalog().add(layerGroup); // retrieve the created layer group by name if (workspace == null) { return getCatalog().getLayerGroupByName(layerGroupName); } return getCatalog().getLayerGroupByName(workspace, layerGroupName); } /** * Helper method that clones vector layer into a certain workspace. */ private LayerInfo cloneVectorLayerIntoWorkspace(WorkspaceInfoImpl workspace, NamespaceInfoImpl nameSpace, String layerName) { Catalog catalog = getCatalog(); // get the original object from the catalog LayerInfo originalLayerInfo = catalog.getLayerByName(layerName); FeatureTypeInfo originalFeatureTypeInfo = (FeatureTypeInfo) originalLayerInfo.getResource(); DataStoreInfo originalStoreInfo = originalFeatureTypeInfo.getStore(); // copy the data store, changing is workspace, id and name DataStoreInfoImpl copyDataStoreInfo = new DataStoreInfoImpl(catalog); OwsUtils.copy(originalStoreInfo, copyDataStoreInfo, DataStoreInfo.class); copyDataStoreInfo.setId(UUID.randomUUID().toString()); copyDataStoreInfo.setName(UUID.randomUUID().toString()); copyDataStoreInfo.setWorkspace(workspace); // copy the feature type info, changing the data store and name space FeatureTypeInfoImpl copyFeatureTypeInfo = new FeatureTypeInfoImpl(catalog); OwsUtils.copy(originalFeatureTypeInfo, copyFeatureTypeInfo, FeatureTypeInfo.class); copyFeatureTypeInfo.setNamespace(nameSpace); copyFeatureTypeInfo.setStore(copyDataStoreInfo); // copy the layer, changing the feature type LayerInfoImpl copyLayerInfo = new LayerInfoImpl(); OwsUtils.copy(originalLayerInfo, copyLayerInfo, LayerInfo.class); copyLayerInfo.setId(layerName); copyLayerInfo.setName(layerName); copyLayerInfo.setResource(copyFeatureTypeInfo); // add everything to the catalog catalog.add(copyDataStoreInfo); catalog.add(copyFeatureTypeInfo); catalog.add(copyLayerInfo); // retrieve the cloned layer by name return catalog.getLayerByName(new NameImpl(nameSpace.getPrefix(), layerName)); } }