/* (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.rest.catalog;
import static org.custommonkey.xmlunit.XMLAssert.assertXpathEvaluatesTo;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.*;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.RegexFileFilter;
import org.custommonkey.xmlunit.SimpleNamespaceContext;
import org.custommonkey.xmlunit.XMLUnit;
import org.custommonkey.xmlunit.XpathEngine;
import org.geoserver.catalog.CoverageInfo;
import org.geoserver.catalog.CoverageStoreInfo;
import org.geoserver.data.test.MockData;
import org.geoserver.data.test.SystemTestData;
import org.geoserver.rest.RestBaseController;
import org.geotools.data.DataUtilities;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletResponse;
import org.w3c.dom.Document;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
public class StructuredCoverageStoresTest extends CatalogRESTTestSupport {
protected static QName WATTEMP = new QName(MockData.WCS_PREFIX, "watertemp", MockData.WCS_PREFIX);
List<File> movedFiles = new ArrayList<>();
private XpathEngine xpath;
private File mosaic;
@BeforeClass
public static void setupTimeZone() {
System.setProperty("user.timezone", "GMT");
}
@Before
public void prepare() {
xpath = XMLUnit.newXpathEngine();
}
@AfterClass
public static void cleanupTimeZone() {
System.clearProperty("user.timezone");
}
@Before
public void setupWaterTemp() throws IOException {
getTestData().addRasterLayer(WATTEMP, "watertemp.zip", null, null, SystemTestData.class, getCatalog());
// drop the stores
getGeoServer().reset();
mosaic = new File(testData.getDataDirectoryRoot(), WATTEMP.getLocalPart());
for (File file : FileUtils.listFiles(mosaic, new RegexFileFilter("NCOM_.*100_.*tiff"), null)) {
File target = new File(file.getParentFile().getParentFile(), file.getName());
movedFiles.add(target);
if(target.exists()) {
assertTrue(target.delete());
}
assertTrue(file.renameTo(target));
}
for (File file : FileUtils.listFiles(mosaic, new RegexFileFilter("watertemp.*"), null)) {
assertTrue(file.delete());
}
}
@Override
protected void onSetUp(SystemTestData testData) throws Exception {
super.onSetUp(testData);
testData.addWorkspace(SystemTestData.WCS_PREFIX, SystemTestData.WCS_URI, getCatalog());
// setup the namespace context for this test
Map<String, String> namespaces = new HashMap<>();
namespaces.put("gml", "http://www.opengis.net/gml");
namespaces.put("gf", "http://www.geoserver.org/rest/granules");
namespaces.put("wfs", "http://www.opengis.net/wfs");
XMLUnit.setXpathNamespaceContext(new SimpleNamespaceContext(namespaces));
}
@Override
protected void setUpTestData(SystemTestData testData) throws Exception {
// nothing to do
testData.setUpSecurity();
}
@Test
public void testIndexResourcesXML() throws Exception {
Document dom = getAsDOM( RestBaseController.ROOT_PATH + "/workspaces/wcs/coveragestores/watertemp.xml", 200);
// print(dom);
assertXpathEvaluatesTo("watertemp", "/coverageStore/name", dom);
dom = getAsDOM( RestBaseController.ROOT_PATH + "/workspaces/wcs/coveragestores/watertemp/coverages/watertemp.xml", 200);
// print(dom);
assertXpathEvaluatesTo("watertemp", "/coverage/name", dom);
assertXpathEvaluatesTo("watertemp", "/coverage/nativeName", dom);
// todo: check there is a link to the index
dom = getAsDOM( RestBaseController.ROOT_PATH + "/workspaces/wcs/coveragestores/watertemp/coverages/watertemp/index.xml", 200);
// print(dom);
assertXpathEvaluatesTo("4", "count(//Schema/attributes/Attribute)", dom);
assertXpathEvaluatesTo("com.vividsolutions.jts.geom.MultiPolygon", "/Schema/attributes/Attribute[name='the_geom']/binding", dom);
assertXpathEvaluatesTo("java.lang.String", "/Schema/attributes/Attribute[name='location']/binding", dom);
assertXpathEvaluatesTo("java.util.Date", "/Schema/attributes/Attribute[name='ingestion']/binding", dom);
assertXpathEvaluatesTo("java.lang.Integer", "/Schema/attributes/Attribute[name='elevation']/binding", dom);
dom = getAsDOM( RestBaseController.ROOT_PATH + "/workspaces/wcs/coveragestores/watertemp/coverages/watertemp/index/granules.xml");
// print(dom);
assertXpathEvaluatesTo("2", "count(//gf:watertemp)", dom);
assertXpathEvaluatesTo("2008-10-31T00:00:00Z", "//gf:watertemp[gf:location = 'NCOM_wattemp_000_20081031T0000000_12.tiff']/gf:ingestion", dom);
assertXpathEvaluatesTo("0", "//gf:watertemp[gf:location = 'NCOM_wattemp_000_20081031T0000000_12.tiff']/gf:elevation", dom);
assertXpathEvaluatesTo("2008-11-01T00:00:00Z", "//gf:watertemp[gf:location = 'NCOM_wattemp_000_20081101T0000000_12.tiff']/gf:ingestion", dom);
assertXpathEvaluatesTo("0", "//gf:watertemp[gf:location = 'NCOM_wattemp_000_20081101T0000000_12.tiff']/gf:elevation", dom);
// get the granules ids
String octoberId = xpath.evaluate("//gf:watertemp[gf:location = 'NCOM_wattemp_000_20081031T0000000_12.tiff']/@fid", dom);
String novemberId = xpath.evaluate("//gf:watertemp[gf:location = 'NCOM_wattemp_000_20081101T0000000_12.tiff']/@fid", dom);
dom = getAsDOM( RestBaseController.ROOT_PATH + "/workspaces/wcs/coveragestores/watertemp/coverages/watertemp/index/granules/" + octoberId + ".xml");
// print(dom);
assertXpathEvaluatesTo(octoberId, "//gf:watertemp/@fid", dom);
assertXpathEvaluatesTo("NCOM_wattemp_000_20081031T0000000_12.tiff", "//gf:watertemp/gf:location", dom);
assertXpathEvaluatesTo("0", "//gf:watertemp/gf:elevation", dom);
dom = getAsDOM( RestBaseController.ROOT_PATH + "/workspaces/wcs/coveragestores/watertemp/coverages/watertemp/index/granules/" + novemberId + ".xml");
// print(dom);
assertXpathEvaluatesTo(novemberId, "//gf:watertemp/@fid", dom);
assertXpathEvaluatesTo("NCOM_wattemp_000_20081101T0000000_12.tiff", "//gf:watertemp/gf:location", dom);
assertXpathEvaluatesTo("0", "//gf:watertemp/gf:elevation", dom);
}
@Test
public void testGranulesOnRenamedCoverage() throws Exception {
// rename the watertemp coverage
CoverageStoreInfo store = catalog.getCoverageStoreByName("watertemp");
CoverageInfo coverage = catalog.getCoverageByCoverageStore(store, "watertemp");
coverage.setName("renamed");
catalog.save(coverage);
Document dom = getAsDOM( RestBaseController.ROOT_PATH + "/workspaces/wcs/coveragestores/watertemp/coverages/renamed/index.xml", 200);
// print(dom);
assertXpathEvaluatesTo("4", "count(//Schema/attributes/Attribute)", dom);
assertXpathEvaluatesTo("com.vividsolutions.jts.geom.MultiPolygon", "/Schema/attributes/Attribute[name='the_geom']/binding", dom);
assertXpathEvaluatesTo("java.lang.String", "/Schema/attributes/Attribute[name='location']/binding", dom);
assertXpathEvaluatesTo("java.util.Date", "/Schema/attributes/Attribute[name='ingestion']/binding", dom);
assertXpathEvaluatesTo("java.lang.Integer", "/Schema/attributes/Attribute[name='elevation']/binding", dom);
}
@Test
public void testIndexResourcesJSON() throws Exception {
JSONObject json = (JSONObject) getAsJSON(RestBaseController.ROOT_PATH + "/workspaces/wcs/coveragestores/watertemp/coverages/watertemp/index.json");
// print(json);
JSONObject schema = json.getJSONObject("Schema");
JSONObject external = schema.getJSONObject("attributes");
JSONArray attributes = external.getJSONArray("Attribute");
assertEquals(4, attributes.size());
assertEquals("com.vividsolutions.jts.geom.MultiPolygon", attributes.getJSONObject(0).get("binding"));
json = (JSONObject) getAsJSON(RestBaseController.ROOT_PATH + "/workspaces/wcs/coveragestores/watertemp/coverages/watertemp/index/granules.json");
// print(json);
JSONArray features = json.getJSONArray("features");
String octoberId = null;
for (int i = 0; i < features.size(); i++) {
JSONObject feature = features.getJSONObject(i);
String location = feature.getJSONObject("properties").getString("location");
if("NCOM_wattemp_000_20081031T0000000_12.tiff".equals(location)) {
octoberId = feature.getString("id");
}
}
json = (JSONObject) getAsJSON( RestBaseController.ROOT_PATH + "/workspaces/wcs/coveragestores/watertemp/coverages/watertemp/index/granules/" + octoberId + ".json", 200);
// print(json);
features = json.getJSONArray("features");
assertEquals(1, features.size());
JSONObject feature = features.getJSONObject(0);
assertEquals(octoberId, feature.get("id"));
JSONObject properties = feature.getJSONObject("properties");
assertEquals("NCOM_wattemp_000_20081031T0000000_12.tiff", properties.get("location"));
assertEquals(0, properties.get("elevation"));
}
@Test
public void testMissingGranule() throws Exception {
MockHttpServletResponse response = getAsServletResponse( RestBaseController.ROOT_PATH + "/workspaces/wcs/coveragestores/watertemp/coverages/watertemp/index/granules/notThere.xml");
assertEquals(404, response.getStatus());
}
@Test
public void testGetWrongGranule() throws Exception {
// Parameters for the request
String ws = "wcs";
String cs = "watertemp";
String g = "notThere";
// Request path
String requestPath = RestBaseController.ROOT_PATH + "/workspaces/" + ws + "/coveragestores/" + cs + "/coverages/" + cs + "/index/granules/" + g;
// Exception path
// First request should thrown an exception
MockHttpServletResponse response = getAsServletResponse(requestPath);
assertEquals(404, response.getStatus());
assertThat(response.getContentAsString(), containsString(g));
// Same request with ?quietOnNotFound should not throw an exception
response = getAsServletResponse(requestPath + "?quietOnNotFound=true");
assertEquals(404, response.getStatus());
// No exception thrown
assertTrue(response.getContentAsString().isEmpty());
}
@Test
public void testDeleteSingleGranule() throws Exception {
Document dom = getAsDOM( RestBaseController.ROOT_PATH + "/workspaces/wcs/coveragestores/watertemp/coverages/watertemp/index/granules.xml");
// print(dom);
// get the granule ids
String octoberId = xpath.evaluate("//gf:watertemp[gf:location = 'NCOM_wattemp_000_20081031T0000000_12.tiff']/@fid", dom);
assertNotNull(octoberId);
// delete it
MockHttpServletResponse response = deleteAsServletResponse(RestBaseController.ROOT_PATH + "/workspaces/wcs/coveragestores/watertemp/coverages/watertemp/index/granules/" + octoberId);
assertEquals(200, response.getStatus());
// check it's gone from the index
dom = getAsDOM( RestBaseController.ROOT_PATH + "/workspaces/wcs/coveragestores/watertemp/coverages/watertemp/index/granules.xml");
assertXpathEvaluatesTo("1", "count(//gf:watertemp)", dom);
assertXpathEvaluatesTo("0", "count(//gf:watertemp[gf:location = 'NCOM_wattemp_000_20081031T0000000_12.tiff'])", dom);
assertXpathEvaluatesTo("2008-11-01T00:00:00Z", "//gf:watertemp[gf:location = 'NCOM_wattemp_000_20081101T0000000_12.tiff']/gf:ingestion", dom);
assertXpathEvaluatesTo("0", "//gf:watertemp[gf:location = 'NCOM_wattemp_000_20081101T0000000_12.tiff']/gf:elevation", dom);
}
@Test
public void testDeleteSingleGranuleGsConfigStyle() throws Exception {
Document dom = getAsDOM( RestBaseController.ROOT_PATH + "/workspaces/wcs/coveragestores/watertemp/coverages/watertemp/index/granules.xml");
// print(dom);
// get the granule ids
String octoberId = xpath.evaluate("//gf:watertemp[gf:location = 'NCOM_wattemp_000_20081031T0000000_12.tiff']/@fid", dom);
assertNotNull(octoberId);
// delete it like gsconfig does (yes, it really appens "./json" at the end)
MockHttpServletResponse response = deleteAsServletResponse(RestBaseController.ROOT_PATH
+ "/workspaces/wcs/coveragestores/watertemp/coverages/watertemp/index/granules/" + octoberId + "/.json");
assertEquals(200, response.getStatus());
// check it's gone from the index
dom = getAsDOM( RestBaseController.ROOT_PATH + "/workspaces/wcs/coveragestores/watertemp/coverages/watertemp/index/granules.xml");
assertXpathEvaluatesTo("1", "count(//gf:watertemp)", dom);
assertXpathEvaluatesTo("0", "count(//gf:watertemp[gf:location = 'NCOM_wattemp_000_20081031T0000000_12.tiff'])", dom);
assertXpathEvaluatesTo("2008-11-01T00:00:00Z", "//gf:watertemp[gf:location = 'NCOM_wattemp_000_20081101T0000000_12.tiff']/gf:ingestion", dom);
assertXpathEvaluatesTo("0", "//gf:watertemp[gf:location = 'NCOM_wattemp_000_20081101T0000000_12.tiff']/gf:elevation", dom);
}
@Test
public void testDeleteAllGranules() throws Exception {
Document dom = getAsDOM( RestBaseController.ROOT_PATH + "/workspaces/wcs/coveragestores/watertemp/coverages/watertemp/index/granules.xml", 200);
assertXpathEvaluatesTo("2", "count(//gf:watertemp)", dom);
// print(dom);
MockHttpServletResponse response = deleteAsServletResponse(RestBaseController.ROOT_PATH + "/workspaces/wcs/coveragestores/watertemp/coverages/watertemp/index/granules");
assertEquals(200, response.getStatus());
// check it's gone from the index
dom = getAsDOM( RestBaseController.ROOT_PATH + "/workspaces/wcs/coveragestores/watertemp/coverages/watertemp/index/granules.xml");
assertXpathEvaluatesTo("0", "count(//gf:watertemp)", dom);
}
@Test
public void testDeleteByFilter() throws Exception {
Document dom = getAsDOM( RestBaseController.ROOT_PATH + "/workspaces/wcs/coveragestores/watertemp/coverages/watertemp/index/granules.xml");
assertXpathEvaluatesTo("2", "count(//gf:watertemp)", dom);
// print(dom);
MockHttpServletResponse response = deleteAsServletResponse(RestBaseController.ROOT_PATH + "/workspaces/wcs/coveragestores/watertemp/coverages" +
"/watertemp/index/granules?filter=ingestion=2008-11-01T00:00:00Z");
assertEquals(200, response.getStatus());
// check it's gone from the index
dom = getAsDOM( RestBaseController.ROOT_PATH + "/workspaces/wcs/coveragestores/watertemp/coverages/watertemp/index/granules.xml");
// print(dom);
assertXpathEvaluatesTo("1", "count(//gf:watertemp)", dom);
assertXpathEvaluatesTo("2008-10-31T00:00:00Z", "//gf:watertemp[gf:location = 'NCOM_wattemp_000_20081031T0000000_12.tiff']/gf:ingestion", dom);
assertXpathEvaluatesTo("0", "//gf:watertemp[gf:location = 'NCOM_wattemp_000_20081031T0000000_12.tiff']/gf:elevation", dom);
}
@Test
public void testHarvestSingle() throws Exception {
File file = movedFiles.get(0);
File target = new File(mosaic, file.getName());
assertTrue(file.renameTo(target));
URL url = DataUtilities.fileToURL(target.getCanonicalFile());
String body = url.toExternalForm();
MockHttpServletResponse response = postAsServletResponse(RestBaseController.ROOT_PATH + "/workspaces/wcs/coveragestores/watertemp/external.imagemosaic",
body, "text/plain");
assertEquals(202, response.getStatus());
Document dom = getAsDOM( RestBaseController.ROOT_PATH + "/workspaces/wcs/coveragestores/watertemp/coverages/watertemp/index/granules.xml");
// print(dom);
assertXpathEvaluatesTo("3", "count(//gf:watertemp)", dom);
assertXpathEvaluatesTo("1", "count(//gf:watertemp[gf:location = '" + file.getName() + "'])", dom);
}
@Test
public void testHarvestMulti() throws Exception {
for (File file : movedFiles) {
File target = new File(mosaic, file.getName());
assertTrue(file.renameTo(target));
}
// re-harvest the entire mosaic (two files refreshed, two files added)
URL url = DataUtilities.fileToURL(mosaic.getCanonicalFile());
String body = url.toExternalForm();
MockHttpServletResponse response = postAsServletResponse(RestBaseController.ROOT_PATH + "/workspaces/wcs/coveragestores/watertemp/external.imagemosaic",
body, "text/plain");
assertEquals(202, response.getStatus());
Document dom = getAsDOM( RestBaseController.ROOT_PATH + "/workspaces/wcs/coveragestores/watertemp/coverages/watertemp/index/granules.xml");
// print(dom);
assertXpathEvaluatesTo("4", "count(//gf:watertemp)", dom);
assertXpathEvaluatesTo("2008-10-31T00:00:00Z", "//gf:watertemp[gf:location = 'NCOM_wattemp_000_20081031T0000000_12.tiff']/gf:ingestion", dom);
assertXpathEvaluatesTo("0", "//gf:watertemp[gf:location = 'NCOM_wattemp_000_20081031T0000000_12.tiff']/gf:elevation", dom);
assertXpathEvaluatesTo("2008-11-01T00:00:00Z", "//gf:watertemp[gf:location = 'NCOM_wattemp_000_20081101T0000000_12.tiff']/gf:ingestion", dom);
assertXpathEvaluatesTo("0", "//gf:watertemp[gf:location = 'NCOM_wattemp_000_20081101T0000000_12.tiff']/gf:elevation", dom);
assertXpathEvaluatesTo("2008-10-31T00:00:00Z", "//gf:watertemp[gf:location = 'NCOM_wattemp_100_20081031T0000000_12.tiff']/gf:ingestion", dom);
assertXpathEvaluatesTo("100", "//gf:watertemp[gf:location = 'NCOM_wattemp_100_20081031T0000000_12.tiff']/gf:elevation", dom);
assertXpathEvaluatesTo("2008-11-01T00:00:00Z", "//gf:watertemp[gf:location = 'NCOM_wattemp_100_20081101T0000000_12.tiff']/gf:ingestion", dom);
assertXpathEvaluatesTo("100", "//gf:watertemp[gf:location = 'NCOM_wattemp_100_20081101T0000000_12.tiff']/gf:elevation", dom);
}
}