Skip to content

Commit

Permalink
[GEOS-7148] WCS 2.0.1 can send coverage in wrong format
Browse files Browse the repository at this point in the history
  • Loading branch information
aaime committed Oct 2, 2015
1 parent eb19c32 commit 46807ea
Show file tree
Hide file tree
Showing 9 changed files with 75 additions and 39 deletions.
9 changes: 2 additions & 7 deletions src/wcs2_0/src/main/java/applicationContext.xml
Expand Up @@ -157,13 +157,8 @@
<constructor-arg ref="catalog" index="1" /> <constructor-arg ref="catalog" index="1" />
</bean> </bean>


<bean id="arcGridWcsMimeMapper" class="org.geoserver.wcs2_0.response.ReaderClassMimeMapper"> <bean id="arcGridWcsMimeMapper" class="org.geoserver.wcs2_0.response.FormatNameMimeMapper">
<constructor-arg index="0" value="org.geotools.gce.arcgrid.ArcGridReader"/> <constructor-arg index="0" value="ArcGrid"/>
<constructor-arg index="1" value="text/plain" />
</bean>

<bean id="gtopo30WcsMimeMapper" class="org.geoserver.wcs2_0.response.ReaderClassMimeMapper">
<constructor-arg index="0" value="org.geotools.gce.gtopo30.GTopo30Reader"/>
<constructor-arg index="1" value="text/plain" /> <constructor-arg index="1" value="text/plain" />
</bean> </bean>


Expand Down
Expand Up @@ -46,7 +46,7 @@ public class DefaultWebCoverageService20 implements WebCoverageService20 {


protected Logger LOGGER = Logging.getLogger(DefaultWebCoverageService20.class); protected Logger LOGGER = Logging.getLogger(DefaultWebCoverageService20.class);


private MIMETypeMapper mimemapper; private MIMETypeMapper mimeMapper;


private Catalog catalog; private Catalog catalog;


Expand All @@ -68,7 +68,7 @@ public DefaultWebCoverageService20(GeoServer geoServer, CoverageResponseDelegate
this.catalog = geoServer.getCatalog(); this.catalog = geoServer.getCatalog();
this.responseFactory = responseFactory; this.responseFactory = responseFactory;
this.envelopeAxesMapper=envelopeDimensionsMapper; this.envelopeAxesMapper=envelopeDimensionsMapper;
this.mimemapper=mimemappe; this.mimeMapper=mimemappe;
this.wcsDescribeCoverageExtensions = GeoServerExtensions this.wcsDescribeCoverageExtensions = GeoServerExtensions
.extensions(WCS20DescribeCoverageExtension.class); .extensions(WCS20DescribeCoverageExtension.class);
this.availableDescribeCovExtensions = wcsDescribeCoverageExtensions != null this.availableDescribeCovExtensions = wcsDescribeCoverageExtensions != null
Expand Down Expand Up @@ -121,7 +121,7 @@ public WCS20DescribeCoverageTransformer describeCoverage(DescribeCoverageType re


WCSInfo wcs = getServiceInfo(); WCSInfo wcs = getServiceInfo();


WCS20DescribeCoverageTransformer describeTransformer = new WCS20DescribeCoverageTransformer(wcs, catalog, responseFactory,envelopeAxesMapper,mimemapper); WCS20DescribeCoverageTransformer describeTransformer = new WCS20DescribeCoverageTransformer(wcs, catalog, responseFactory,envelopeAxesMapper,mimeMapper);
describeTransformer.setEncoding(Charset.forName(wcs.getGeoServer().getSettings().getCharset())); describeTransformer.setEncoding(Charset.forName(wcs.getGeoServer().getSettings().getCharset()));
return describeTransformer; return describeTransformer;
} }
Expand All @@ -135,7 +135,7 @@ public GridCoverage getCoverage(GetCoverageType request) {
throw new OWS20Exception("Required parameter coverageId missing", WCS20Exception.WCS20ExceptionCode.EmptyCoverageIdList, "coverageId"); throw new OWS20Exception("Required parameter coverageId missing", WCS20Exception.WCS20ExceptionCode.EmptyCoverageIdList, "coverageId");
} }


return new GetCoverage(getServiceInfo(), catalog,envelopeAxesMapper).run(request); return new GetCoverage(getServiceInfo(), catalog, envelopeAxesMapper, mimeMapper).run(request);
} }


@Override @Override
Expand Down
16 changes: 15 additions & 1 deletion src/wcs2_0/src/main/java/org/geoserver/wcs2_0/GetCoverage.java
Expand Up @@ -40,6 +40,7 @@
import org.geoserver.wcs2_0.exception.WCS20Exception.WCS20ExceptionCode; import org.geoserver.wcs2_0.exception.WCS20Exception.WCS20ExceptionCode;
import org.geoserver.wcs2_0.response.DimensionBean; import org.geoserver.wcs2_0.response.DimensionBean;
import org.geoserver.wcs2_0.response.GranuleStackImpl; import org.geoserver.wcs2_0.response.GranuleStackImpl;
import org.geoserver.wcs2_0.response.MIMETypeMapper;
import org.geoserver.wcs2_0.response.WCSDimensionsSubsetHelper; import org.geoserver.wcs2_0.response.WCSDimensionsSubsetHelper;
import org.geoserver.wcs2_0.util.EnvelopeAxesLabelsMapper; import org.geoserver.wcs2_0.util.EnvelopeAxesLabelsMapper;
import org.geoserver.wcs2_0.util.NCNameResourceCodec; import org.geoserver.wcs2_0.util.NCNameResourceCodec;
Expand Down Expand Up @@ -153,17 +154,20 @@ public class GetCoverage {
/** Factory used to create new coverages */ /** Factory used to create new coverages */
private GridCoverageFactory gridCoverageFactory; private GridCoverageFactory gridCoverageFactory;


private MIMETypeMapper mimeMapper;

public final static String SRS_STARTER="http://www.opengis.net/def/crs/EPSG/0/"; public final static String SRS_STARTER="http://www.opengis.net/def/crs/EPSG/0/";


/** Hints to indicate that a scale has been pre-applied, reporting the scaling factors */ /** Hints to indicate that a scale has been pre-applied, reporting the scaling factors */
public static Hints.Key PRE_APPLIED_SCALE = new Hints.Key(Double[].class); public static Hints.Key PRE_APPLIED_SCALE = new Hints.Key(Double[].class);


private static final double EPS = 1e-6; private static final double EPS = 1e-6;


public GetCoverage(WCSInfo serviceInfo, Catalog catalog, EnvelopeAxesLabelsMapper envelopeDimensionsMapper) { public GetCoverage(WCSInfo serviceInfo, Catalog catalog, EnvelopeAxesLabelsMapper envelopeDimensionsMapper, MIMETypeMapper mimeMapper) {
this.wcs = serviceInfo; this.wcs = serviceInfo;
this.catalog = catalog; this.catalog = catalog;
this.envelopeDimensionsMapper=envelopeDimensionsMapper; this.envelopeDimensionsMapper=envelopeDimensionsMapper;
this.mimeMapper = mimeMapper;


// building the needed URI CRS Factories // building the needed URI CRS Factories
Hints hints = GeoTools.getDefaultHints(); Hints hints = GeoTools.getDefaultHints();
Expand Down Expand Up @@ -207,6 +211,16 @@ public GridCoverage run(GetCoverageType request) {
if(LOGGER.isLoggable(Level.FINE)){ if(LOGGER.isLoggable(Level.FINE)){
LOGGER.fine("Executing GetCoverage request on coverage :"+linfo.toString()); LOGGER.fine("Executing GetCoverage request on coverage :"+linfo.toString());
} }

// prepare the default format
if(request.getFormat() == null) {
try {
String nativeFormat = mimeMapper.mapNativeFormat(cinfo);
request.setFormat(nativeFormat);
} catch(Exception e) {
LOGGER.log(Level.WARNING, "Could not compute the native type of the coverage, defaulting to image/tiff", e);
}
}


// === k, now start the execution // === k, now start the execution
GridCoverage coverage = null; GridCoverage coverage = null;
Expand Down
Expand Up @@ -18,21 +18,21 @@
* @author Andrea Aime - GeoSolutions * @author Andrea Aime - GeoSolutions
* *
*/ */
public class ReaderClassMimeMapper implements CoverageMimeTypeMapper { public class FormatNameMimeMapper implements CoverageMimeTypeMapper {


private String mime; private String formatName;


private Class<? extends GridCoverageReader> readerClass; private String mime;


public ReaderClassMimeMapper(Class<? extends GridCoverageReader> readerClass, String mime) { public FormatNameMimeMapper(String formatName, String mime) {
this.readerClass = readerClass; this.formatName = formatName;
this.mime = mime; this.mime = mime;
} }


@Override @Override
public String getMimeType(CoverageInfo ci) throws IOException { public String getMimeType(CoverageInfo ci) throws IOException {
GridCoverageReader reader = ci.getGridCoverageReader(null, null); GridCoverageReader reader = ci.getGridCoverageReader(null, null);
if (reader != null && readerClass.isAssignableFrom(reader.getClass())) { if (formatName.equals(reader.getFormat().getName())) {
return mime; return mime;
} }


Expand Down
Expand Up @@ -60,8 +60,13 @@ public String getMimeType(CoverageInfo cInfo) throws IOException {
if (LOGGER.isLoggable(Level.FINE)) { if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("Found reader for format: " + reader.getFormatName()); LOGGER.fine("Found reader for format: " + reader.getFormatName());
} }
return reader.getOriginatingProvider().getMIMETypes()[0]; String mime = reader.getOriginatingProvider().getMIMETypes()[0];

// the native format rules says "the range set values can be obtained unaltered",
// so we cannot allow lossy compressions (which would alter the range set values)
String lcMime = mime.toLowerCase();
if(lcMime.contains("jpeg") || lcMime.contains("mrsid") || lcMime.contains("ecw")) {
return null;
}
} }
} catch (Exception e) { } catch (Exception e) {
if (LOGGER.isLoggable(Level.WARNING)) { if (LOGGER.isLoggable(Level.WARNING)) {
Expand Down
Expand Up @@ -23,6 +23,7 @@
import org.geoserver.platform.GeoServerExtensions; import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.wcs.responses.CoverageResponseDelegate; import org.geoserver.wcs.responses.CoverageResponseDelegate;
import org.geoserver.wcs.responses.CoverageResponseDelegateFinder; import org.geoserver.wcs.responses.CoverageResponseDelegateFinder;
import org.geoserver.wcs.responses.GeoTIFFCoverageResponseDelegate;
import org.geotools.util.SoftValueHashMap; import org.geotools.util.SoftValueHashMap;
import org.geotools.util.Utilities; import org.geotools.util.Utilities;
import org.geotools.util.logging.Logging; import org.geotools.util.logging.Logging;
Expand All @@ -42,6 +43,8 @@
public class MIMETypeMapper implements ApplicationContextAware { public class MIMETypeMapper implements ApplicationContextAware {


private static final String NO_MIME_TYPE = "NoMimeType"; private static final String NO_MIME_TYPE = "NoMimeType";

public static final String DEFAULT_FORMAT = GeoTIFFCoverageResponseDelegate.GEOTIFF_CONTENT_TYPE;


private Logger LOGGER = Logging.getLogger(MIMETypeMapper.class); private Logger LOGGER = Logging.getLogger(MIMETypeMapper.class);


Expand All @@ -67,8 +70,9 @@ private MIMETypeMapper(CoverageResponseDelegateFinder finder, Catalog catalog) {
} }


/** /**
* Returns a mime types or null for the provided {@link CoverageInfo} using the * Returns a mime types for the provided {@link CoverageInfo} using the
* {@link CoverageInfo#getNativeFormat()} as its key. * {@link CoverageInfo#getNativeFormat()} as its key. In case none was found,
* the DEFAULT_FORMAT format is returned.
* *
* @param cInfo the {@link CoverageInfo} to find a mime type for * @param cInfo the {@link CoverageInfo} to find a mime type for
* @return a mime types or null for the provided {@link CoverageInfo} using the * @return a mime types or null for the provided {@link CoverageInfo} using the
Expand All @@ -82,7 +86,7 @@ public String mapNativeFormat(final CoverageInfo cInfo) throws IOException {
String mime = mimeTypeCache.get(cInfo.getId()); String mime = mimeTypeCache.get(cInfo.getId());
if(mime != null) { if(mime != null) {
if(NO_MIME_TYPE.equals(mime)) { if(NO_MIME_TYPE.equals(mime)) {
return null; return DEFAULT_FORMAT;
} else { } else {
return mime; return mime;
} }
Expand All @@ -95,6 +99,7 @@ public String mapNativeFormat(final CoverageInfo cInfo) throws IOException {
} }
} }


// the native format must be encodable
if (mime != null && outputMimeTypes.contains(mime)) { if (mime != null && outputMimeTypes.contains(mime)) {
mimeTypeCache.put(cInfo.getId(), mime); mimeTypeCache.put(cInfo.getId(), mime);
if (LOGGER.isLoggable(Level.FINE)) { if (LOGGER.isLoggable(Level.FINE)) {
Expand All @@ -105,7 +110,7 @@ public String mapNativeFormat(final CoverageInfo cInfo) throws IOException {
// we either don't have a clue about the mime, or we don't have an encoder, // we either don't have a clue about the mime, or we don't have an encoder,
// save the response as null // save the response as null
mimeTypeCache.put(cInfo.getId(), NO_MIME_TYPE); mimeTypeCache.put(cInfo.getId(), NO_MIME_TYPE);
return null; return DEFAULT_FORMAT;
} }
} }


Expand Down
Expand Up @@ -306,10 +306,6 @@ private void handleServiceParameters(CoverageInfo ci) throws IOException {
element("wcs:CoverageSubtype", "RectifiedGridCoverage"); element("wcs:CoverageSubtype", "RectifiedGridCoverage");


String mapNativeFormat = mimemapper.mapNativeFormat(ci); String mapNativeFormat = mimemapper.mapNativeFormat(ci);
if(mapNativeFormat==null){
// fall back on GeoTiff when a native format cannot be determined
mapNativeFormat = GeoTIFFCoverageResponseDelegate.GEOTIFF_CONTENT_TYPE;
}
element("wcs:nativeFormat",mapNativeFormat); element("wcs:nativeFormat",mapNativeFormat);
end("wcs:ServiceParameters"); end("wcs:ServiceParameters");
} }
Expand Down
Expand Up @@ -77,6 +77,7 @@ public void testCustomUnit() throws Exception {
assertXpathEvaluatesTo("1", "count(//wcs:CoverageDescription/gmlcov:rangeType/swe:DataRecord/swe:field)", dom); assertXpathEvaluatesTo("1", "count(//wcs:CoverageDescription/gmlcov:rangeType/swe:DataRecord/swe:field)", dom);
assertXpathEvaluatesTo("rain", "//wcs:CoverageDescription/gmlcov:rangeType//swe:DataRecord/swe:field/@name", dom); assertXpathEvaluatesTo("rain", "//wcs:CoverageDescription/gmlcov:rangeType//swe:DataRecord/swe:field/@name", dom);
assertXpathEvaluatesTo("mm", "//wcs:CoverageDescription/gmlcov:rangeType/swe:DataRecord/swe:field/swe:Quantity/swe:uom/@code", dom); assertXpathEvaluatesTo("mm", "//wcs:CoverageDescription/gmlcov:rangeType/swe:DataRecord/swe:field/swe:Quantity/swe:uom/@code", dom);
assertXpathEvaluatesTo("text/plain", "//wcs:CoverageDescriptions//wcs:CoverageDescription[1]//wcs:ServiceParameters//wcs:nativeFormat", dom);
} }


@Test @Test
Expand Down Expand Up @@ -218,7 +219,7 @@ public void testNativeFormatArcGrid() throws Exception {
assertXpathEvaluatesTo("1", "count(//wcs:CoverageDescription)", dom); assertXpathEvaluatesTo("1", "count(//wcs:CoverageDescription)", dom);
assertXpathEvaluatesTo("sf__rain", "//wcs:CoverageDescriptions/wcs:CoverageDescription[1]/@gml:id", dom); assertXpathEvaluatesTo("sf__rain", "//wcs:CoverageDescriptions/wcs:CoverageDescription[1]/@gml:id", dom);
assertXpathEvaluatesTo("1", "count(//wcs:CoverageDescription[1]//gmlcov:rangeType//swe:DataRecord//swe:field)", dom); assertXpathEvaluatesTo("1", "count(//wcs:CoverageDescription[1]//gmlcov:rangeType//swe:DataRecord//swe:field)", dom);
assertXpathEvaluatesTo("image/tiff", "//wcs:CoverageDescriptions/wcs:CoverageDescription[1]//wcs:ServiceParameters//wcs:nativeFormat", dom); assertXpathEvaluatesTo("text/plain", "//wcs:CoverageDescriptions/wcs:CoverageDescription[1]//wcs:ServiceParameters//wcs:nativeFormat", dom);
} }


@Test @Test
Expand Down
Expand Up @@ -4,6 +4,18 @@


import java.util.Map; import java.util.Map;


import javax.xml.namespace.QName;

import org.eclipse.emf.common.util.EList;
import org.geoserver.data.test.MockData;
import org.geoserver.data.test.SystemTestData;
import org.geoserver.wcs2_0.WCS20Const;
import org.geotools.wcs.v2_0.RangeSubset;
import org.geotools.wcs.v2_0.Scaling;
import org.junit.Test;

import com.mockrunner.mock.web.MockHttpServletResponse;

import net.opengis.wcs20.GetCoverageType; import net.opengis.wcs20.GetCoverageType;
import net.opengis.wcs20.InterpolationType; import net.opengis.wcs20.InterpolationType;
import net.opengis.wcs20.RangeItemType; import net.opengis.wcs20.RangeItemType;
Expand All @@ -17,16 +29,15 @@
import net.opengis.wcs20.TargetAxisExtentType; import net.opengis.wcs20.TargetAxisExtentType;
import net.opengis.wcs20.TargetAxisSizeType; import net.opengis.wcs20.TargetAxisSizeType;


import org.eclipse.emf.common.util.EList;
import org.geoserver.wcs2_0.WCS20Const;
import org.geotools.wcs.v2_0.RangeSubset;
import org.geotools.wcs.v2_0.Scaling;
import org.junit.Test;
import org.w3c.dom.Document;

import com.mockrunner.mock.web.MockHttpServletResponse;

public class GetCoverageKvpTest extends WCSKVPTestSupport { public class GetCoverageKvpTest extends WCSKVPTestSupport {

private static final QName RAIN = new QName(MockData.SF_URI, "rain", MockData.SF_PREFIX);

@Override
protected void onSetUp(SystemTestData testData) throws Exception {
super.onSetUp(testData);
testData.addRasterLayer(RAIN, "rain.zip", "asc", getCatalog());
}


@Test @Test
public void testParseBasic() throws Exception { public void testParseBasic() throws Exception {
Expand All @@ -38,11 +49,20 @@ public void testParseBasic() throws Exception {
@Test @Test
public void testGetCoverageNoWs() throws Exception { public void testGetCoverageNoWs() throws Exception {
MockHttpServletResponse response = getAsServletResponse("wcs?request=GetCoverage&service=WCS&version=2.0.1" + MockHttpServletResponse response = getAsServletResponse("wcs?request=GetCoverage&service=WCS&version=2.0.1" +
"&coverageId=BlueMarble&&Format=image/tiff"); "&coverageId=BlueMarble&Format=image/tiff");


assertEquals("image/tiff", response.getContentType()); assertEquals("image/tiff", response.getContentType());
} }


@Test
public void testGetCoverageNativeFormat() throws Exception {
MockHttpServletResponse response = getAsServletResponse("wcs?request=GetCoverage&service=WCS&version=2.0.1" +
"&coverageId=sf__rain");

// we got back an ArcGrid response
assertEquals("text/plain", response.getContentType());
}

@Test @Test
public void testNotExistent() throws Exception { public void testNotExistent() throws Exception {
MockHttpServletResponse response = getAsServletResponse("wcs?request=GetCoverage&service=WCS&version=2.0.1" + MockHttpServletResponse response = getAsServletResponse("wcs?request=GetCoverage&service=WCS&version=2.0.1" +
Expand Down

0 comments on commit 46807ea

Please sign in to comment.