org.geoserver.rest.catalog.DataStoreFileUploadTest.java Source code

Java tutorial

Introduction

Here is the source code for org.geoserver.rest.catalog.DataStoreFileUploadTest.java

Source

/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
 * This code is licensed under the GPL 2.0 license, available at the root
 * application directory.
 */
package org.geoserver.rest.catalog;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.DataStoreInfo;
import org.geoserver.catalog.NamespaceInfo;
import org.geoserver.catalog.WorkspaceInfo;
import org.geoserver.catalog.impl.NamespaceInfoImpl;
import org.geoserver.catalog.impl.WorkspaceInfoImpl;
import org.geoserver.data.test.MockData;
import org.geoserver.data.test.SystemTestData;
import org.geoserver.filters.LoggingFilter;
import org.geoserver.platform.GeoServerResourceLoader;
import org.geoserver.platform.resource.Files;
import org.geotools.data.DataUtilities;
import org.h2.tools.DeleteDbFiles;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletResponse;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.servlet.Filter;
import java.io.*;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import static org.geoserver.rest.RestBaseController.ROOT_PATH;
import static org.junit.Assert.*;

public class DataStoreFileUploadTest extends CatalogRESTTestSupport {
    @Override
    protected void onSetUp(SystemTestData testData) throws Exception {
        super.onSetUp(testData);

        NamespaceInfo gsmlNamespace = new NamespaceInfoImpl();
        gsmlNamespace.setPrefix("gsml");
        gsmlNamespace.setURI("http://www.cgi-iugs.org/xml/GeoSciML/2");

        WorkspaceInfo gsmlWorkspace = new WorkspaceInfoImpl();
        gsmlWorkspace.setName("gsml");

        getCatalog().add(gsmlNamespace);
        getCatalog().add(gsmlWorkspace);
    }

    @Override
    protected List<Filter> getFilters() {
        LoggingFilter filter = new LoggingFilter();
        filter.setEnabled(true);
        filter.setLogBodies(true);
        return Collections.singletonList(filter);
    }

    @Before
    public void removePdsDataStore() {
        removeStore("gs", "pds");
        removeStore("gs", "store with spaces");
    }

    @After
    public void cleanUpDbFiles() throws Exception {
        DeleteDbFiles.execute("target", "foo", true);
        DeleteDbFiles.execute("target", "pds", true);
        DeleteDbFiles.execute("target", "chinese_poly", true);
    }

    @Test
    public void testPropertyFileUpload() throws Exception {
        /*
        Properties p = new Properties();
        p.put( "_", "name:String,pointProperty:Point");
        p.put( "pds.0", "'zero'|POINT(0 0)");
        p.put( "pds.1", "'one'|POINT(1 1)");
        */
        byte[] bytes = propertyFile();
        //p.store( output, null );

        put(ROOT_PATH + "/workspaces/gs/datastores/pds/file.properties", bytes, "text/plain");
        Document dom = getAsDOM("wfs?request=getfeature&typename=gs:pds");
        assertFeatures(dom);
    }

    @Test
    public void testPropertyFileUploadWithWorkspace() throws Exception {
        byte[] bytes = propertyFile();

        put(ROOT_PATH + "/workspaces/sf/datastores/pds/file.properties", bytes, "text/plain");
        Document dom = getAsDOM("wfs?request=getfeature&typename=sf:pds");
        assertFeatures(dom, "sf");
    }

    @Test
    public void testPropertyFileUploadZipped() throws Exception {
        byte[] bytes = propertyFile();

        //compress
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ZipOutputStream zout = new ZipOutputStream(out);
        zout.putNextEntry(new ZipEntry("pds.properties"));
        zout.write(bytes);
        zout.flush();
        zout.close();

        put(ROOT_PATH + "/workspaces/gs/datastores/pds/file.properties", out.toByteArray(), "application/zip");

        Document dom = getAsDOM("wfs?request=getfeature&typename=gs:pds");
        assertFeatures(dom);

    }

    byte[] propertyFile() throws IOException {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output));
        writer.write("_=name:String,pointProperty:Point\n");
        writer.write("ds.0='zero'|POINT(0 0)\n");
        writer.write("ds.1='one'|POINT(1 1)\n");
        writer.flush();
        return output.toByteArray();
    }

    void assertFeatures(Document dom) throws Exception {
        assertFeatures(dom, "gs");
    }

    void assertFeatures(Document dom, String ns) throws Exception {
        assertEquals("wfs:FeatureCollection", dom.getDocumentElement().getNodeName());
        assertEquals(2, dom.getElementsByTagName(ns + ":pds").getLength());
    }

    @Test
    public void testShapeFileUpload() throws Exception {
        byte[] bytes = shpZipAsBytes();
        put(ROOT_PATH + "/workspaces/gs/datastores/pds/file.shp", bytes, "application/zip");
        Document dom = getAsDOM("wfs?request=getfeature&typename=gs:pds");
        assertFeatures(dom);
    }

    @Test
    public void testShapeFileUploadWithCharset() throws Exception {
        /* Requires that a zipped shapefile (chinese_poly.zip) be in test-data directory */
        byte[] bytes = shpChineseZipAsBytes();
        MockHttpServletResponse response = putAsServletResponse(
                ROOT_PATH + "/workspaces/gs/datastores/chinese_poly/file.shp?charset=UTF-8", bytes,
                "application/zip");
        assertEquals(201, response.getStatus());

        MockHttpServletResponse response2 = getAsServletResponse("wfs?request=getfeature&typename=gs:chinese_poly",
                "GB18030");
        assertTrue(response2.getContentAsString().contains("\u951f\u65a4\u62f7"));
    }

    byte[] shpZipAsBytes() throws IOException {
        return toBytes(getClass().getResourceAsStream("test-data/pds.zip"));
    }

    byte[] shpChineseZipAsBytes() throws IOException {
        return toBytes(getClass().getResourceAsStream("test-data/chinese_poly.zip"));
    }

    byte[] shpMultiZipAsBytes() throws IOException {
        return toBytes(getClass().getResourceAsStream("test-data/pdst.zip"));
    }

    byte[] toBytes(InputStream in) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();

        int c;
        while ((c = in.read()) != -1) {
            out.write(c);
        }
        return out.toByteArray();
    }

    @Test
    public void testShapeFileUploadExternal() throws Exception {
        Document dom = getAsDOM("wfs?request=getfeature&typename=gs:pds");
        assertEquals("ows:ExceptionReport", dom.getDocumentElement().getNodeName());

        File target = new File("target");
        File f = File.createTempFile("rest", "dir", target);
        try {
            f.delete();
            f.mkdir();

            File zip = new File(f, "pds.zip");
            IOUtils.copy(getClass().getResourceAsStream("test-data/pds.zip"), new FileOutputStream(zip));
            org.geoserver.rest.util.IOUtils.inflate(new ZipFile(zip), Files.asResource(f), null);

            MockHttpServletResponse resp = putAsServletResponse(
                    ROOT_PATH + "/workspaces/gs/datastores/pds/external.shp",
                    new File(f, "pds.shp").toURL().toString(), "text/plain");
            assertEquals(201, resp.getStatus());

            dom = getAsDOM("wfs?request=getfeature&typename=gs:pds");
            assertFeatures(dom);

            // try to download it again after a full reload from disk (GEOS-4616)
            getGeoServer().reload();

            resp = getAsServletResponse(ROOT_PATH + "/workspaces/gs/datastores/pds/file.shp");
            assertEquals(200, resp.getStatus());
            assertEquals("application/zip", resp.getContentType());

            Set<String> entryNames = new HashSet<>();
            try (ByteArrayInputStream bin = getBinaryInputStream(resp);
                    ZipInputStream zin = new ZipInputStream(bin)) {
                ZipEntry entry;
                while ((entry = zin.getNextEntry()) != null) {
                    entryNames.add(entry.getName());
                }
            }
            assertTrue(entryNames.contains("pds.shp"));
            assertTrue(entryNames.contains("pds.shx"));
            assertTrue(entryNames.contains("pds.dbf"));
        } finally {
            FileUtils.deleteQuietly(f);
        }
    }

    @Test
    public void testShapeFileUploadNotExisting() throws Exception {
        File file = new File("./target/notThere.tiff");
        if (file.exists()) {
            assertTrue(file.delete());
        }

        URL url = DataUtilities.fileToURL(file.getCanonicalFile());
        String body = url.toExternalForm();
        MockHttpServletResponse response = putAsServletResponse(
                ROOT_PATH + "/workspaces/gs/datastores/pds/external.shp", body, "text/plain");
        assertEquals(400, response.getStatus());
    }

    @Test
    public void testShapeFileUploadIntoExisting() throws Exception {
        Catalog cat = getCatalog();
        assertNull(cat.getDataStoreByName("gs", "foo_h2"));

        String xml = "<dataStore>" + " <name>foo_h2</name>" + " <type>H2</type>" + " <connectionParameters>"
                + "<namespace>" + MockData.DEFAULT_URI + "</namespace>" + "<database>target/foo</database>"
                + "<dbtype>h2</dbtype>" + " </connectionParameters>" + "<workspace>gs</workspace>" + "</dataStore>";

        post(ROOT_PATH + "/workspaces/gs/datastores", xml);

        DataStoreInfo ds = cat.getDataStoreByName("gs", "foo_h2");
        assertNotNull(ds);

        assertTrue(cat.getFeatureTypesByDataStore(ds).isEmpty());

        byte[] bytes = shpZipAsBytes();
        put(ROOT_PATH + "/workspaces/gs/datastores/foo_h2/file.shp", bytes, "application/zip");

        assertFalse(cat.getFeatureTypesByDataStore(ds).isEmpty());

        Document dom = getAsDOM("wfs?request=getfeature&typename=gs:pds");
        assertFeatures(dom);
    }

    @Test
    public void testShapeFileUploadWithTarget() throws Exception {
        Catalog cat = getCatalog();
        assertNull(cat.getDataStoreByName("gs", "pds"));

        byte[] bytes = shpZipAsBytes();
        put(ROOT_PATH + "/workspaces/gs/datastores/pds/file.shp?target=h2", bytes, "application/zip");

        DataStoreInfo ds = cat.getDataStoreByName("gs", "pds");
        assertNotNull(ds);
        assertFalse(cat.getFeatureTypesByDataStore(ds).isEmpty());

        Document dom = getAsDOM("wfs?request=getfeature&typename=gs:pds");
        assertFeatures(dom);
    }

    @Test
    @Ignore
    // fixing https://osgeo-org.atlassian.net/browse/GEOS-6845, re-enable when a proper fix for spaces in
    // name has been made
    public void testShapeFileUploadWithSpaces() throws Exception {
        Catalog cat = getCatalog();
        assertNull(cat.getDataStoreByName("gs", "store with spaces"));

        byte[] bytes = shpZipAsBytes();
        put(ROOT_PATH + "/workspaces/gs/datastores/store%20with%20spaces/file.shp", bytes, "application/zip");

        DataStoreInfo ds = cat.getDataStoreByName("gs", "store with spaces");
        assertNull(ds);
    }

    @Test
    public void testShapefileUploadMultiple() throws Exception {
        Catalog cat = getCatalog();
        assertNull(cat.getDataStoreByName("gs", "pdst"));

        put(ROOT_PATH + "/workspaces/gs/datastores/pdst/file.shp?configure=all", shpMultiZipAsBytes(),
                "application/zip");

        DataStoreInfo ds = cat.getDataStoreByName("gs", "pdst");
        assertNotNull(ds);

        assertEquals(2, cat.getFeatureTypesByDataStore(ds).size());
    }

    @Test
    public void testGetProperties() throws Exception {
        MockHttpServletResponse resp = getAsServletResponse(
                ROOT_PATH + "/workspaces/gs/datastores/pds/file.properties");
        assertEquals(404, resp.getStatus());

        byte[] bytes = propertyFile();
        put(ROOT_PATH + "/workspaces/gs/datastores/pds/file.properties", bytes, "text/plain");

        resp = getAsServletResponse(ROOT_PATH + "/workspaces/gs/datastores/pds/file.properties");
        assertEquals(200, resp.getStatus());
        assertEquals("application/zip", resp.getContentType());

        ByteArrayInputStream bin = getBinaryInputStream(resp);
        ZipInputStream zin = new ZipInputStream(bin);

        ZipEntry entry = zin.getNextEntry();
        assertNotNull(entry);
        assertEquals("pds.properties", entry.getName());
    }

    @Test
    public void testAppSchemaMappingFileUpload() throws Exception {
        byte[] bytes = appSchemaMappingAsBytes();
        if (bytes == null) {
            // skip test
            LOGGER.warning("app-schema test data not available: skipping test");
            return;
        }

        // copy necessary .properties files from classpath
        loadAppSchemaTestData();

        // upload mapping file (datastore is created implicitly)
        put(ROOT_PATH + "/workspaces/gsml/datastores/mappedPolygons/file.appschema", bytes, "text/xml");
        Document dom = getAsDOM("wfs?request=getfeature&typename=gsml:MappedFeature");

        // print(dom);

        assertEquals("wfs:FeatureCollection", dom.getDocumentElement().getNodeName());
        NodeList mappedFeatureNodes = dom.getDocumentElement()
                .getElementsByTagNameNS("http://www.cgi-iugs.org/xml/GeoSciML/2", "MappedFeature");
        assertNotNull(mappedFeatureNodes);
        assertEquals(2, mappedFeatureNodes.getLength());

        int namesCount = countNameAttributes(mappedFeatureNodes.item(0));
        assertEquals(2, namesCount);

        // upload alternative mapping file
        bytes = appSchemaAlternativeMappingAsBytes();
        put(ROOT_PATH + "/workspaces/gsml/datastores/mappedPolygons/file.appschema?configure=none", bytes,
                "text/xml");
        dom = getAsDOM("wfs?request=getfeature&typename=gsml:MappedFeature");

        // print(dom);

        assertEquals("wfs:FeatureCollection", dom.getDocumentElement().getNodeName());
        mappedFeatureNodes = dom.getDocumentElement()
                .getElementsByTagNameNS("http://www.cgi-iugs.org/xml/GeoSciML/2", "MappedFeature");
        assertNotNull(mappedFeatureNodes);
        assertEquals(2, mappedFeatureNodes.getLength());

        namesCount = countNameAttributes(mappedFeatureNodes.item(0));
        // just one name should be found
        assertEquals(1, namesCount);
    }

    private int countNameAttributes(Node mappedFeatureNode) {
        NodeList attrNodes = mappedFeatureNode.getChildNodes();
        int namesCount = 0;
        for (int i = 0; i < attrNodes.getLength(); i++) {
            Node attribute = attrNodes.item(i);
            if ("name".equals(attribute.getLocalName())) {
                namesCount++;
            }
        }

        return namesCount;
    }

    private void loadAppSchemaTestData() throws IOException {
        GeoServerResourceLoader loader = new GeoServerResourceLoader(getTestData().getDataDirectoryRoot());
        loader.copyFromClassPath("test-data/mappedPolygons.properties", "data/gsml/mappedPolygons.properties");
        loader.copyFromClassPath("test-data/mappedPolygons.oasis.xml", "data/gsml/mappedPolygons.oasis.xml");
        loader.copyFromClassPath("test-data/commonSchemas_new/GeoSciML/CGI_basicTypes.xsd",
                "data/gsml/commonSchemas_new/GeoSciML/CGI_basicTypes.xsd");
        loader.copyFromClassPath("test-data/commonSchemas_new/GeoSciML/CGI_Value.xsd",
                "data/gsml/commonSchemas_new/GeoSciML/CGI_Value.xsd");
        loader.copyFromClassPath("test-data/commonSchemas_new/GeoSciML/earthMaterial.xsd",
                "data/gsml/commonSchemas_new/GeoSciML/earthMaterial.xsd");
        loader.copyFromClassPath("test-data/commonSchemas_new/GeoSciML/fossil.xsd",
                "data/gsml/commonSchemas_new/GeoSciML/fossil.xsd");
        loader.copyFromClassPath("test-data/commonSchemas_new/GeoSciML/geologicStructure.xsd",
                "data/gsml/commonSchemas_new/GeoSciML/geologicStructure.xsd");
        loader.copyFromClassPath("test-data/commonSchemas_new/GeoSciML/geologicUnit.xsd",
                "data/gsml/commonSchemas_new/GeoSciML/geologicUnit.xsd");
        loader.copyFromClassPath("test-data/commonSchemas_new/GeoSciML/geosciml.xsd",
                "data/gsml/commonSchemas_new/GeoSciML/geosciml.xsd");
        loader.copyFromClassPath("test-data/commonSchemas_new/GeoSciML/Gsml.xsd",
                "data/gsml/commonSchemas_new/GeoSciML/Gsml.xsd");
        loader.copyFromClassPath("test-data/commonSchemas_new/GeoSciML/metadata.xsd",
                "data/gsml/commonSchemas_new/GeoSciML/metadata.xsd");
        loader.copyFromClassPath("test-data/commonSchemas_new/GeoSciML/ObsAndMeas.xsd",
                "data/gsml/commonSchemas_new/GeoSciML/ObsAndMeas.xsd");
        loader.copyFromClassPath("test-data/commonSchemas_new/GeoSciML/vocabulary.xsd",
                "data/gsml/commonSchemas_new/GeoSciML/vocabulary.xsd");
    }

    private byte[] appSchemaMappingAsBytes() throws IOException {
        InputStream in = getClass().getResourceAsStream("/test-data/mappedPolygons.xml");
        if (in != null) {
            byte[] original = toBytes(in);
            byte[] modified;

            String originalAsString = new String(original, Charset.forName("UTF-8"));
            // modify paths in the original mapping file
            String modifiedAsString = originalAsString.replace("file:./", "file:../")
                    .replace("commonSchemas_new/", "../commonSchemas_new/")
                    .replace("mappedPolygons.oasis", "../mappedPolygons.oasis");

            modified = modifiedAsString.getBytes(Charset.forName("UTF-8"));

            return modified;
        } else {
            return null;
        }
    }

    private byte[] appSchemaAlternativeMappingAsBytes() throws Exception {
        byte[] mapping = appSchemaMappingAsBytes();
        if (mapping != null) {
            Document mappingDom = dom(new ByteArrayInputStream(mapping));

            // remove mapping for MappedFeature/gml:name[2] attribute
            NodeList attrMappingNodes = mappingDom.getDocumentElement().getElementsByTagName("AttributeMapping");
            for (int i = 0; i < attrMappingNodes.getLength(); i++) {
                Node attrMapping = attrMappingNodes.item(i);
                NodeList children = attrMapping.getChildNodes();
                for (int j = 0; j < children.getLength(); j++) {
                    if ("MappedFeature/gml:name[2]".equals(children.item(j).getTextContent())) {
                        attrMapping.getParentNode().removeChild(attrMapping);
                        break;
                    }
                }
            }

            ByteArrayOutputStream output = new ByteArrayOutputStream();
            print(mappingDom, output);

            return output.toByteArray();
        } else {
            return null;
        }
    }
}