/** * Copyright (c) Codice Foundation * <p> * This 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 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 * Lesser General Public License for more details. A copy of the GNU Lesser General Public License * is distributed along with this program and can be found at * <http://www.gnu.org/licenses/lgpl.html>. **/ package org.codice.ddf.spatial.ogc.wfs.v1_0_0.catalog.source.reader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import javax.ws.rs.Consumes; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.ResponseBuilder; import javax.ws.rs.ext.MessageBodyReader; import javax.ws.rs.ext.Provider; import org.apache.commons.io.IOUtils; import org.codice.ddf.spatial.ogc.wfs.catalog.common.WfsFeatureCollection; import org.codice.ddf.spatial.ogc.wfs.catalog.converter.FeatureConverter; import org.codice.ddf.spatial.ogc.wfs.catalog.converter.impl.GmlEnvelopeConverter; import org.codice.ddf.spatial.ogc.wfs.catalog.converter.impl.GmlGeometryConverter; import org.codice.ddf.spatial.ogc.wfs.v1_0_0.catalog.converter.impl.FeatureCollectionConverterWfs10; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.XStreamException; import com.thoughtworks.xstream.io.xml.WstxDriver; import ddf.catalog.data.Metacard; @Consumes({MediaType.TEXT_XML, MediaType.APPLICATION_XML}) @Provider public class FeatureCollectionMessageBodyReaderWfs10 implements MessageBodyReader<WfsFeatureCollection> { private static final Logger LOGGER = LoggerFactory.getLogger( FeatureCollectionMessageBodyReaderWfs10.class); protected XStream xstream; protected FeatureCollectionConverterWfs10 featureCollectionConverter; protected Map<String, FeatureConverter> featureConverterMap = new HashMap<String, FeatureConverter>(); public FeatureCollectionMessageBodyReaderWfs10() { xstream = new XStream(new WstxDriver()); xstream.setClassLoader(this.getClass() .getClassLoader()); xstream.registerConverter(new GmlGeometryConverter()); xstream.registerConverter(new GmlEnvelopeConverter()); xstream.alias("FeatureCollection", WfsFeatureCollection.class); featureCollectionConverter = new FeatureCollectionConverterWfs10(); featureCollectionConverter.setFeatureConverterMap(featureConverterMap); xstream.registerConverter(featureCollectionConverter); } @Override public boolean isReadable(Class<?> clazz, Type type, Annotation[] annotations, MediaType mediaType) { if (!WfsFeatureCollection.class.isAssignableFrom(clazz)) { LOGGER.debug("{} class is not readable", clazz); } return WfsFeatureCollection.class.isAssignableFrom(clazz); } @Override public WfsFeatureCollection readFrom(Class<WfsFeatureCollection> clazz, Type type, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> headers, InputStream inStream) throws IOException, WebApplicationException { // Save original input stream for any exception message that might need to be // created String originalInputStream = IOUtils.toString(inStream, StandardCharsets.UTF_8.name()); // Re-create the input stream (since it has already been read for potential // exception message creation) inStream = new ByteArrayInputStream(originalInputStream.getBytes(StandardCharsets.UTF_8.name())); WfsFeatureCollection featureCollection = null; try { featureCollection = (WfsFeatureCollection) xstream.fromXML(inStream); } catch (XStreamException e) { // If a ServiceExceptionReport is sent from the remote WFS site it will be sent with an // JAX-RS "OK" status, hence the ErrorResponse exception mapper will not fire. // Instead the ServiceExceptionReport will come here and be treated like a GetFeature // response, resulting in an XStreamException since ExceptionReport cannot be // unmarshalled. So this catch clause is responsible for catching that XStream // exception and creating a JAX-RS response containing the original stream // (with the ExceptionReport) and rethrowing it as a WebApplicationException, // which CXF will wrap as a ClientException that the WfsSource catches, converts // to a WfsException, and logs. LOGGER.debug("Exception unmarshalling", e); ByteArrayInputStream bis = new ByteArrayInputStream(originalInputStream.getBytes( StandardCharsets.UTF_8)); ResponseBuilder responseBuilder = Response.ok(bis); responseBuilder.type("text/xml"); Response response = responseBuilder.build(); throw new WebApplicationException(e, response); } finally { IOUtils.closeQuietly(inStream); } return featureCollection; } public void registerConverter(FeatureConverter converter) { featureConverterMap.put(converter.getMetacardType() .getName(), converter); xstream.registerConverter(converter); xstream.alias(converter.getMetacardType() .getName(), Metacard.class); } }