org.geowebcache.service.wmts.WMTSRestTest.java Source code

Java tutorial

Introduction

Here is the source code for org.geowebcache.service.wmts.WMTSRestTest.java

Source

/**
 * This program is free software: you can redistribute it and/or modify it under the terms of the
 * GNU Lesser General Public License as published by the Free Software Foundation, either version 3
 * of the License, or (at your option) any later version.
 *
 * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * <p>You should have received a copy of the GNU Lesser General Public License along with this
 * program. If not, see <http://www.gnu.org/licenses/>.
 *
 * @author Sandro Salari, GeoSolutions S.A.S., Copyright 2017
 */
package org.geowebcache.service.wmts;

import static org.custommonkey.xmlunit.XMLAssert.assertXpathEvaluatesTo;
import static org.custommonkey.xmlunit.XMLAssert.assertXpathExists;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.io.FileUtils;
import org.custommonkey.xmlunit.SimpleNamespaceContext;
import org.custommonkey.xmlunit.XMLUnit;
import org.custommonkey.xmlunit.XpathEngine;
import org.geowebcache.config.ServerConfiguration;
import org.geowebcache.config.XMLGridSubset;
import org.geowebcache.config.legends.LegendInfo;
import org.geowebcache.config.legends.LegendInfoBuilder;
import org.geowebcache.conveyor.Conveyor;
import org.geowebcache.conveyor.ConveyorTile;
import org.geowebcache.filter.parameters.StringParameterFilter;
import org.geowebcache.filter.security.SecurityDispatcher;
import org.geowebcache.grid.BoundingBox;
import org.geowebcache.grid.GridSet;
import org.geowebcache.grid.GridSetBroker;
import org.geowebcache.grid.GridSubset;
import org.geowebcache.grid.SRS;
import org.geowebcache.io.ByteArrayResource;
import org.geowebcache.io.Resource;
import org.geowebcache.layer.TileLayer;
import org.geowebcache.layer.TileLayerDispatcher;
import org.geowebcache.mime.MimeType;
import org.geowebcache.service.OWSException;
import org.geowebcache.stats.RuntimeStats;
import org.geowebcache.storage.StorageBroker;
import org.geowebcache.util.ResponseUtils;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.context.annotation.Bean;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.w3c.dom.Document;

public class WMTSRestTest {

    // private static Map<String, String> namespaces = new HashMap<String, String>(0);
    private static XpathEngine xpath;

    GridSetBroker broker = new GridSetBroker(true, true);
    private WMTSService wmtsService;
    private StorageBroker storageBroker;
    private TileLayerDispatcher tileLayerDispatcher;
    private SecurityDispatcher securityDispatcher;
    private RuntimeStats runtimeStats;

    @Before
    public void setupMockService() throws Exception {
        this.storageBroker = mock(StorageBroker.class);
        this.tileLayerDispatcher = tileLayerDispatcher();
        this.runtimeStats = mock(RuntimeStats.class);
        this.wmtsService = new WMTSService(storageBroker, tileLayerDispatcher, broker, runtimeStats);
        this.securityDispatcher = securityDispatcher();
        wmtsService.setSecurityDispatcher(securityDispatcher);
    }

    @BeforeClass
    public static void setupXMLUnit() {
        Map<String, String> namespaces = new HashMap<>();
        namespaces.put("xlink", "http://www.w3.org/1999/xlink");
        namespaces.put("xsi", "http://www.w3.org/2001/XMLSchema-instance");
        namespaces.put("ows", "http://www.opengis.net/ows/1.1");
        namespaces.put("wmts", "http://www.opengis.net/wmts/1.0");
        XMLUnit.setXpathNamespaceContext(new SimpleNamespaceContext(namespaces));
        xpath = XMLUnit.newXpathEngine();
    }

    @Test
    public void testGetCap() throws Exception {
        MockHttpServletRequest req = new MockHttpServletRequest();
        req.setPathInfo("geowebcache/service/wmts/rest/WMTSCapabilities.xml");
        MockHttpServletResponse resp = dispatch(req);

        assertEquals(200, resp.getStatus());
        assertEquals("text/xml", resp.getContentType());
        final Document doc = XMLUnit.buildTestDocument(resp.getContentAsString());
        assertXpathExists("//wmts:Contents/wmts:Layer", doc);
        assertXpathExists("//wmts:Contents/wmts:Layer[ows:Identifier='mockLayer']", doc);
        assertXpathEvaluatesTo("2", "count(//wmts:Contents/wmts:Layer/wmts:Style/ows:Identifier)", doc);
        assertXpathExists("//wmts:Contents/wmts:Layer/wmts:Style[ows:Identifier='style-a']", doc);
        assertXpathExists("//wmts:Contents/wmts:Layer/wmts:Style[ows:Identifier='style-b']/wmts:LegendURL"
                + "[@width='125'][@height='130'][@format='image/png']"
                + "[@minScaleDenominator='5000.0'][@maxScaleDenominator='10000.0']"
                + "[@xlink:href='https://some-url?some-parameter=value3&another-parameter=value4']", doc);
        assertXpathExists("//wmts:Contents/wmts:Layer/wmts:ResourceURL[@resourceType='tile']"
                + "[@format='image/jpeg']" + "[@template='http://localhost/service/wmts/rest/"
                + "mockLayer/{style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}?format=image/jpeg&time={time}&elevation={elevation}']",
                doc);
        assertXpathExists("//wmts:Contents/wmts:Layer/wmts:ResourceURL[@resourceType='FeatureInfo']"
                + "[@format='text/plain']" + "[@template='http://localhost/service/wmts/rest"
                + "/mockLayer/{style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}/{J}/{I}?format=text/plain&time={time}&elevation={elevation}']",
                doc);
        assertXpathExists("//wmts:ServiceMetadataURL[@xlink:href='http://localhost/service/wmts/rest"
                + "/WMTSCapabilities.xml']", doc);
    }

    @Test
    public void testGetTileWithStyle() throws Exception {
        MockHttpServletRequest req = new MockHttpServletRequest();
        req.setPathInfo("geowebcache/service/wmts/rest/mockLayer/style-a/EPSG:4326/EPSG:4326:0/0/0");
        req.addParameter("format", "image/png");

        MockHttpServletResponse resp = dispatch(req);

        assertEquals(200, resp.getStatus());
        assertEquals("image/png", resp.getContentType());
        assertEquals("EPSG:4326", resp.getHeader("geowebcache-crs"));
        assertArrayEquals(getSampleTileContent().getContents(), resp.getContentAsByteArray());
    }

    public MockHttpServletResponse dispatch(MockHttpServletRequest req) throws Exception {
        MockHttpServletResponse resp = new MockHttpServletResponse();
        try {
            final Conveyor conveyor = wmtsService.getConveyor(req, resp);

            if (conveyor.reqHandler == Conveyor.RequestHandler.SERVICE) {
                wmtsService.handleRequest(conveyor);
            } else {
                ResponseUtils.writeTile(securityDispatcher(), conveyor, conveyor.getLayerId(),
                        tileLayerDispatcher(), null, runtimeStats);
            }
        } catch (OWSException e) {
            ResponseUtils.writeFixedResponse(resp, e.getResponseCode(), e.getContentType(), e.getResponse(),
                    Conveyor.CacheResult.OTHER, runtimeStats);
        }

        return resp;
    }

    @Test
    public void testGetTileWithoutStyle() throws Exception {
        MockHttpServletRequest req = new MockHttpServletRequest();
        req.setPathInfo("geowebcache/service/wmts/rest/mockLayer/EPSG:4326/EPSG:4326:0/0/0");
        req.addParameter("format", "image/png");

        final MockHttpServletResponse resp = dispatch(req);
        assertEquals(200, resp.getStatus());
    }

    @Test
    public void testGetTileWithEmptyStyle() throws Exception {
        MockHttpServletRequest req = new MockHttpServletRequest();
        req.setPathInfo("geowebcache/service/wmts/rest/mockLayer//EPSG:4326/EPSG:4326:0/0/0");
        req.addParameter("format", "image/png");

        final MockHttpServletResponse resp = dispatch(req);
        assertEquals(200, resp.getStatus());
    }

    @Test
    public void testGetInfoWithStyle() throws Exception {
        MockHttpServletRequest req = new MockHttpServletRequest();
        req.setPathInfo("geowebcache/service/wmts/rest/mockLayer/style-a/EPSG:4326/EPSG:4326:0/0/0/0/0");
        req.addParameter("format", "text/plain");

        final MockHttpServletResponse resp = dispatch(req);

        assertEquals(200, resp.getStatus());
        assertEquals("text/plain", resp.getContentType());
    }

    @Test
    public void testGetInfoWithoutStyle() throws Exception {
        MockHttpServletRequest req = new MockHttpServletRequest();
        req.setPathInfo("geowebcache/service/wmts/rest/mockLayer/EPSG:4326/EPSG:4326:0/0/0/0/0");
        req.addParameter("format", "text/plain");

        final MockHttpServletResponse resp = dispatch(req);

        assertEquals(200, resp.getStatus());
        assertEquals("text/plain", resp.getContentType());
    }

    @Test
    public void testOWSException() throws Exception {
        MockHttpServletRequest req = new MockHttpServletRequest();
        req.setPathInfo("geowebcache/service/wmts/rest/mockLayer/EPSG:4326/EPSG:4326:0/0/0/0/0");
        req.addParameter("format", "text/none");

        MockHttpServletResponse resp = dispatch(req);

        assertEquals(HttpStatus.SC_BAD_REQUEST, resp.getStatus());
        assertEquals("text/xml", resp.getContentType());

        Document doc = XMLUnit.buildTestDocument(resp.getContentAsString());
        assertXpathExists("//ows:ExceptionReport/ows:Exception[@exceptionCode='InvalidParameterValue']", doc);
    }

    @FunctionalInterface
    public interface TestToExecute {
        void execute() throws Exception;
    }

    @Test
    public void testGetCapabilitiesWithCiteValidation() throws Exception {
        testCiteValidationIsSuccessful(this::testGetCap);
    }

    @Test
    public void testGetTileWithCiteValidation() throws Exception {
        testCiteValidationIsSuccessful(() -> {
            testGetTileWithStyle();
            testGetTileWithoutStyle();
        });
    }

    @Test
    public void testGetInfoWithCiteValidation() throws Exception {
        testCiteValidationIsSuccessful(() -> {
            testGetInfoWithStyle();
            testGetInfoWithoutStyle();
        });
    }

    /** Helper method that just executes the provided test with CITE validation activated. */
    private void testCiteValidationIsSuccessful(TestToExecute request) throws Exception {
        // mock server configuration to activate CITE compliance checks
        ServerConfiguration configuration = mock(ServerConfiguration.class);
        when(configuration.isWmtsCiteCompliant()).thenReturn(true);
        ServerConfiguration previousConfiguration = wmtsService.getMainConfiguration();
        wmtsService.setMainConfiguration(configuration);
        try {
            // the following test should be successful
            request.execute();
        } finally {
            // set whatever was the previous server configuration used by WMTS service
            wmtsService.setMainConfiguration(previousConfiguration);
        }
    }

    public TileLayerDispatcher tileLayerDispatcher() throws Exception {
        TileLayerDispatcher tld = mock(TileLayerDispatcher.class);
        List<String> gridSetNames = Arrays.asList("GlobalCRS84Pixel", "GlobalCRS84Scale", "EPSG:4326");
        String layerName = "mockLayer";
        TileLayer tileLayer = mock(TileLayer.class);

        StringParameterFilter styles = new StringParameterFilter();
        styles.setKey("STYLES");
        styles.setValues(Arrays.asList("style-a", "style-b"));

        StringParameterFilter time = new StringParameterFilter();
        time.setKey("time");
        time.setValues(Arrays.asList("2016-02-23T03:00:00.000Z"));

        StringParameterFilter elevation = new StringParameterFilter();
        elevation.setKey("elevation");
        elevation.setValues(Arrays.asList("500"));

        when(tileLayer.getParameterFilters()).thenReturn(Arrays.asList(styles, time, elevation));

        LegendInfo legendInfo2 = new LegendInfoBuilder().withStyleName("styla-b-legend").withWidth(125)
                .withHeight(130).withFormat("image/png")
                .withCompleteUrl("https://some-url?some-parameter=value3&another-parameter=value4")
                .withMinScale(5000D).withMaxScale(10000D).build();
        when(tileLayer.getLayerLegendsInfo()).thenReturn(Collections.singletonMap("style-b", legendInfo2));

        when(tld.getTileLayer(eq(layerName))).thenReturn(tileLayer);
        when(tileLayer.getName()).thenReturn(layerName);
        when(tileLayer.isEnabled()).thenReturn(true);
        when(tileLayer.isAdvertised()).thenReturn(true);

        final MimeType mimeType1 = MimeType.createFromFormat("image/png");
        final MimeType mimeType2 = MimeType.createFromFormat("image/jpeg");
        when(tileLayer.getMimeTypes()).thenReturn(Arrays.asList(mimeType1, mimeType2));

        final MimeType infoMimeType1 = MimeType.createFromFormat("text/plain");
        final MimeType infoMimeType2 = MimeType.createFromFormat("text/html");
        final MimeType infoMimeType3 = MimeType.createFromFormat("application/vnd.ogc.gml");
        when(tileLayer.getInfoMimeTypes()).thenReturn(Arrays.asList(infoMimeType1, infoMimeType2, infoMimeType3));
        Map<String, GridSubset> subsets = new HashMap<String, GridSubset>();
        Map<SRS, List<GridSubset>> bySrs = new HashMap<SRS, List<GridSubset>>();

        for (String gsetName : gridSetNames) {
            GridSet gridSet = broker.get(gsetName);
            XMLGridSubset xmlGridSubset = new XMLGridSubset();
            String gridSetName = gridSet.getName();
            xmlGridSubset.setGridSetName(gridSetName);
            GridSubset gridSubSet = xmlGridSubset.getGridSubSet(broker);
            subsets.put(gsetName, gridSubSet);

            List<GridSubset> list = bySrs.get(gridSet.getSrs());
            if (list == null) {
                list = new ArrayList<GridSubset>();
                bySrs.put(gridSet.getSrs(), list);
            }
            list.add(gridSubSet);

            when(tileLayer.getGridSubset(eq(gsetName))).thenReturn(gridSubSet);
        }

        for (SRS srs : bySrs.keySet()) {
            List<GridSubset> list = bySrs.get(srs);
            when(tileLayer.getGridSubsetsForSRS(eq(srs))).thenReturn(list);
        }
        when(tileLayer.getGridSubsets()).thenReturn(subsets.keySet());

        when(tld.getLayerList()).thenReturn(Arrays.asList(tileLayer));

        when(tileLayer.getTile(any(ConveyorTile.class))).thenAnswer(new Answer<ConveyorTile>() {
            @Override
            public ConveyorTile answer(InvocationOnMock invocation) throws Throwable {
                ConveyorTile sourceTile = (ConveyorTile) invocation.getArguments()[0];
                sourceTile.setBlob(getSampleTileContent());
                return sourceTile;
            }
        });

        when(tileLayer.getFeatureInfo(any(ConveyorTile.class), any(BoundingBox.class), anyInt(), anyInt(), anyInt(),
                anyInt())).thenAnswer(new Answer<Resource>() {
                    @Override
                    public Resource answer(InvocationOnMock invocation) throws Throwable {
                        return new ByteArrayResource(new byte[0]);
                    }
                });

        return tld;
    }

    public ByteArrayResource getSampleTileContent() throws IOException, URISyntaxException {
        return new ByteArrayResource(
                FileUtils.readFileToByteArray(new File(getClass().getResource("/image.png").toURI())));
    }

    @Bean
    public SecurityDispatcher securityDispatcher() {
        SecurityDispatcher secDisp = mock(SecurityDispatcher.class);
        when(secDisp.isSecurityEnabled()).thenReturn(false);
        return secDisp;
    }
}