/* (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.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.OutputStream; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.custommonkey.xmlunit.SimpleNamespaceContext; import org.custommonkey.xmlunit.XMLUnit; import org.custommonkey.xmlunit.XpathEngine; import org.geoserver.catalog.DataStoreInfo; import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.catalog.NamespaceInfo; import org.geoserver.catalog.WorkspaceInfo; import org.geoserver.data.test.SystemTestData; import org.geoserver.data.util.IOUtils; import org.geoserver.rest.catalog.CatalogRESTTestSupport; import org.geoserver.test.onlineTest.setup.AppSchemaTestOracleSetup; import org.geoserver.test.onlineTest.setup.AppSchemaTestPostgisSetup; import org.geoserver.test.onlineTest.support.AbstractReferenceDataSetup; import org.geoserver.wfs.WFSInfo; import org.geotools.data.complex.AppSchemaDataAccessRegistry; import org.geotools.data.complex.DataAccessRegistry; import org.geotools.xml.AppSchemaXSDRegistry; import org.geotools.xml.resolver.SchemaCache; import org.junit.Test; import org.springframework.mock.web.MockHttpServletResponse; import org.w3c.dom.Document; /** * Test REST configuration of app-schema. Note that the mapping and properties file are still copied * locally. * * @author Ben Caradoc-Davies (CSIRO Earth Science and Resource Engineering) */ public class RestconfigWfsTest extends CatalogRESTTestSupport { @Override protected void onSetUp(org.geoserver.data.test.SystemTestData testData) throws Exception { super.onSetUp(testData); WFSInfo wfs = getGeoServer().getService(WFSInfo.class); wfs.setCanonicalSchemaLocation(true); wfs.setEncodeFeatureMember(true); getGeoServer().save(wfs); // disable schema caching in tests, as schemas are expected to provided on the classpath SchemaCache.disableAutomaticConfiguration(); } @Override @SuppressWarnings("deprecated") protected void onTearDown(SystemTestData testData) throws Exception { super.onTearDown(testData); DataAccessRegistry.unregisterAll(); AppSchemaDataAccessRegistry.clearAppSchemaProperties(); AppSchemaXSDRegistry.getInstance().dispose(); } private static final String WORKSPACE = "<workspace>" // + "<name>gsml</name>"// + "</workspace>"; private static final String NAMESPACE = "<namespace>" // + "<uri>urn:cgi:xmlns:CGI:GeoSciML:2.0</uri>" // + "</namespace>"; private static final String DATASTORE = "<dataStore>" // + "<name>MappedFeature</name>" // + "<enabled>true</enabled>" // + "<workspace>" // + "<name>gsml</name>" // + "</workspace>" // + "<connectionParameters>" // + "<entry key='dbtype'>app-schema</entry>" // + "<entry key='url'>file:workspaces/gsml/MappedFeature/MappedFeature.xml</entry>" // + "<entry key='namespace'>urn:cgi:xmlns:CGI:GeoSciML:2.0</entry>" // + "</connectionParameters>" // + "</dataStore>"; private static final String FEATURETYPE = "<featureType>" // + "<name>MappedFeature</name>" // + "<nativeName>MappedFeature</nativeName>" // + "<namespace>" // + "<prefix>gsml</prefix>" // + "</namespace>" // + "<title>... TITLE ...</title>" // + "<abstract>... ABSTRACT ...</abstract>" // + "<srs>EPSG:4326</srs>" // + "<latLonBoundingBox>" // + "<minx>-180</minx>" // + "<maxx>180</maxx>" // + "<miny>-90</miny>" // + "<maxy>90</maxy>" // + "<crs>EPSG:4326</crs>" // + "</latLonBoundingBox>" // + "<projectionPolicy>REPROJECT_TO_DECLARED</projectionPolicy>" // + "<enabled>true</enabled>" // + "<metadata>" // + "<entry key='kml.regionateFeatureLimit'>10</entry>" // + "<entry key='indexingEnabled'>false</entry>" // + "<entry key='cachingEnabled'>false</entry>" // + "</metadata>" // + "<store class='dataStore'>" // + "<name>MappedFeature</name>" // + "</store>" // + "<maxFeatures>0</maxFeatures>" // + "<numDecimals>0</numDecimals>" // + "</featureType>"; public static final String DS_PARAMETERS = "<parameters>" // + "<Parameter>" // + "<name>directory</name>" // + "<value>file:./</value>" // + "</Parameter>" // + "</parameters>"; // public static final String MAPPING = "<as:AppSchemaDataAccess xmlns:as='http://www.geotools.org/app-schema'>" // + "<namespaces>" // + "<Namespace>" // + "<prefix>gsml</prefix>" // + "<uri>urn:cgi:xmlns:CGI:GeoSciML:2.0</uri>" // + "</Namespace>" // + "</namespaces>" // + "<sourceDataStores>" // + "<DataStore>" // + "<id>datastore</id>" // + DS_PARAMETERS + "</DataStore>" // + "</sourceDataStores>" // + "<targetTypes>" // + "<FeatureType>" // + "<schemaUri>http://www.geosciml.org/geosciml/2.0/xsd/geosciml.xsd</schemaUri>" // + "</FeatureType>" // + "</targetTypes>" // + "<typeMappings>" // + "<FeatureTypeMapping>" // + "<sourceDataStore>datastore</sourceDataStore>" // + "<sourceType>MAPPEDFEATURE</sourceType>" // + "<targetElement>gsml:MappedFeature</targetElement>" // + "<attributeMappings>" // + "<AttributeMapping>" // + "<targetAttribute>gsml:shape</targetAttribute>" // + "<sourceExpression>" // + "<OCQL>SHAPE</OCQL>"// + "</sourceExpression>" // + "</AttributeMapping>" // + "</attributeMappings>" // + "</FeatureTypeMapping>" // + "</typeMappings>" // + "</as:AppSchemaDataAccess>"; public static final String PROPERTIES = "_=SHAPE:Geometry:srid=4326\n" // + "mf.1=POINT(0 1)\n" // + "mf.2=POINT(2 3)\n"; /** * Test that REST can be used to configure an app-schema datastore and that this datastore can * be used to service a WFS request. */ @Test public void testRestconfig() throws Exception { MockHttpServletResponse response; // create workspace response = postAsServletResponse("/rest/workspaces", WORKSPACE, "text/xml"); assertEquals(201, response.getStatus()); WorkspaceInfo ws = getCatalog().getWorkspaceByName("gsml"); assertNotNull(ws); // set namespace uri (response is 200 as update not create) // (default http://gsml was created when workspace was created) response = putAsServletResponse("/rest/namespaces/gsml", NAMESPACE, "text/xml"); assertEquals(200, response.getStatus()); NamespaceInfo ns = getCatalog().getNamespaceByPrefix("gsml"); assertNotNull(ns); assertEquals("urn:cgi:xmlns:CGI:GeoSciML:2.0", ns.getURI()); // create datastore response = postAsServletResponse("/rest/workspaces/gsml/datastores", DATASTORE, "text/xml"); assertEquals(201, response.getStatus()); DataStoreInfo ds = getCatalog().getDataStoreByName("gsml", "MappedFeature"); assertNotNull(ds); // copy the mapping and properties files copyFiles(); // create featuretype response = postAsServletResponse( "/rest/workspaces/gsml/datastores/MappedFeature/featuretypes", FEATURETYPE, "text/xml"); assertEquals(201, response.getStatus()); FeatureTypeInfo ft = getCatalog().getFeatureTypeByName("gsml", "MappedFeature"); assertNotNull(ft); // test that features can be obtained via WFS Document doc = getAsDOM("wfs?request=GetFeature&version=1.1.0&typename=gsml:MappedFeature"); LOGGER.info("WFS GetFeature&typename=gsml:MappedFeature response:\n" + prettyString(doc)); assertEquals("wfs:FeatureCollection", doc.getDocumentElement().getNodeName()); assertXpathCount(2, "//gsml:MappedFeature", doc); } /** * Copy the mapping and properties files to the data directory. */ private void copyFiles() throws Exception { File dir = new File(new File(new File(getTestData().getDataDirectoryRoot(), "workspaces"), "gsml"), "MappedFeature"); dir.mkdirs(); File propertiesFile = new File(dir, "MAPPEDFEATURE.properties"); IOUtils.copy(new ByteArrayInputStream(PROPERTIES.getBytes("UTF-8")), propertiesFile); String mapping = MAPPING; String onlineTestId = System.getProperty("testDatabase"); if (onlineTestId != null) { // if test if running in online mode, need to use db params onlineTestId = onlineTestId.trim().toLowerCase(); Map<String, File> propertyFiles = new HashMap<String, File>(); propertyFiles.put(propertiesFile.getName(), dir); AbstractReferenceDataSetup setup; if (onlineTestId.equals("oracle")) { // oracle mapping = mapping.replaceAll(DS_PARAMETERS, Matcher .quoteReplacement(AppSchemaTestOracleSetup.DB_PARAMS)); setup = AppSchemaTestOracleSetup.getInstance(propertyFiles); } else { // postgis mapping = mapping.replaceAll(DS_PARAMETERS, Matcher .quoteReplacement(AppSchemaTestPostgisSetup.DB_PARAMS)); setup = AppSchemaTestPostgisSetup.getInstance(propertyFiles); } // Run the sql script to create the tables from properties files setup.setUp(); setup.tearDown(); } IOUtils.copy(new ByteArrayInputStream(mapping.getBytes("UTF-8")), new File(dir, "MappedFeature.xml")); } /** * Return {@link Document} as a pretty-printed string. * * @param document * document to be prettified * @return the prettified string */ private String prettyString(Document document) { OutputStream output = new ByteArrayOutputStream(); prettyPrint(document, output); return output.toString(); } /** * Pretty-print a {@link Document} to an {@link OutputStream}. * * @param document * document to be prettified * @param output * stream to which output is written */ private void prettyPrint(Document document, OutputStream output) { try { Transformer tx = TransformerFactory.newInstance().newTransformer(); tx.setOutputProperty(OutputKeys.INDENT, "yes"); tx.transform(new DOMSource(document), new StreamResult(output)); } catch (Exception e) { throw new RuntimeException(e); } } /** * Assert that there are count occurrences of xpath. * * @param count * @param xpath * @param document */ private void assertXpathCount(int count, String xpath, Document document) throws Exception { XpathEngine xpathEngine = XMLUnit.newXpathEngine(); Map<String, String> namespaces = new HashMap<String, String>(); namespaces.put("wfs", "http://www.opengis.net/wfs"); namespaces.put("gsml", "urn:cgi:xmlns:CGI:GeoSciML:2.0"); xpathEngine.setNamespaceContext(new SimpleNamespaceContext(namespaces)); assertEquals(count, xpathEngine.getMatchingNodes(xpath, document).getLength()); } }