/**
* 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.csw.catalog.common.source;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.core.Response;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import org.apache.commons.lang.StringUtils;
import org.apache.cxf.common.util.CollectionUtils;
import org.apache.cxf.helpers.IOUtils;
import org.apache.cxf.jaxrs.client.ResponseExceptionMapper;
import org.apache.cxf.jaxrs.provider.JAXBElementProvider;
import org.codice.ddf.spatial.ogc.csw.catalog.common.CswException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.opengis.ows.v_1_0_0.ExceptionReport;
import net.opengis.ows.v_1_0_0.ExceptionType;
/**
* Maps the exception report returned as a response from a CSW service into a {@link CswException}.
*
* The format of the XML error response is specified by, and shall validate against, the exception
* response schema defined in clause 8 of the OWS Common Implementation Specification. One or more
* exceptions can be contained in the exception report. See section 10.3.7 of the CSW specification
* for details.
*
* An example of an exception report returned by a CSW service would be:
*
* <pre>
* {@code
* <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
* <ows:ExceptionReport version="1.2.0" xmlns:ns16="http://www.opengis.net/ows/1.1" xmlns:dc="http://purl.org/dc/elements/1.1/"
* xmlns:cat="http://www.opengis.net/cat/csw" xmlns:gco="http://www.isotc211.org/2005/gco" xmlns:gmd="http://www.isotc211.org/2005/gmd"
* xmlns:fra="http://www.cnig.gouv.fr/2005/fra" xmlns:ins="http://www.inspire.org" xmlns:gmx="http://www.isotc211.org/2005/gmx"
* xmlns:ogc="http://www.opengis.net/ogc" xmlns:dct="http://purl.org/dc/terms/" xmlns:ows="http://www.opengis.net/ows"
* xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:gml="http://www.opengis.net/gml" xmlns:csw="http://www.opengis.net/cat/csw/2.0.2"
* xmlns:gmi="http://www.isotc211.org/2005/gmi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
* <ows:Exception exceptionCode="OPERATION_NOT_SUPPORTED">
* <ows:ExceptionText>The XML request is not valid.
* Cause:unexpected element (uri:"", local:"GetRecords"). Expected elements are ...
* </ows:Exception>
* </ows:ExceptionReport>
* }
* </pre>
*
* @author rodgersh
*
*/
public class CswResponseExceptionMapper implements ResponseExceptionMapper<CswException> {
private static final Logger LOGGER = LoggerFactory.getLogger(CswResponseExceptionMapper.class);
@Override
public CswException fromResponse(Response response) {
CswException cswException = null;
if (response != null) {
if (response.getEntity() instanceof InputStream) {
String msg = null;
try {
InputStream is = (InputStream) response.getEntity();
if (is.markSupported()) {
is.reset();
}
msg = IOUtils.toString(is);
} catch (IOException e) {
cswException = new CswException(
"Error received from remote Csw server" + (msg != null ?
": " + msg :
""));
LOGGER.info("Unable to parse exception report: {}", e.getMessage());
LOGGER.debug("Unable to parse exception report: {}", e);
}
if (msg != null) {
try {
JAXBElementProvider<ExceptionReport> provider =
new JAXBElementProvider<ExceptionReport>();
Unmarshaller um = provider.getJAXBContext(ExceptionReport.class,
ExceptionReport.class)
.createUnmarshaller();
XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory();
xmlInputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES,
false);
xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
xmlInputFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES,
false);
XMLStreamReader xmlStreamReader =
xmlInputFactory.createXMLStreamReader(new StringReader(msg));
ExceptionReport report =
(ExceptionReport) um.unmarshal(xmlStreamReader);
cswException = convertToCswException(report);
} catch (JAXBException | XMLStreamException e) {
cswException = new CswException(
"Error received from remote Csw server: " + msg, e);
LOGGER.info("Error parsing the exception report: {}", e.getMessage());
LOGGER.debug("Error parsing the exception report", e);
}
}
} else {
cswException = new CswException(
"Error reading response, entity type not understood: "
+ response.getEntity()
.getClass()
.getName());
}
cswException.setHttpStatus(response.getStatus());
} else {
cswException = new CswException("Error handling response, response is null");
}
return cswException;
}
private CswException convertToCswException(ExceptionReport report) {
CswException cswException = null;
List<ExceptionType> list = new ArrayList<ExceptionType>(report.getException());
for (ExceptionType exceptionType : list) {
String exceptionCode = exceptionType.getExceptionCode();
String locator = exceptionType.getLocator();
List<String> exceptionText = exceptionType.getExceptionText();
StringBuilder exceptionMsg = new StringBuilder();
// Exception code is required per CSW schema, but check it anyway
if (StringUtils.isNotBlank(exceptionCode)) {
exceptionMsg.append("exceptionCode = " + exceptionCode + "\n");
} else {
exceptionMsg.append("exceptionCode = UNSPECIFIED");
}
// Locator and exception text(s) are both optional
if (StringUtils.isNotBlank(locator)) {
exceptionMsg.append("locator = " + locator + "\n");
}
if (!CollectionUtils.isEmpty(exceptionText)) {
for (String text : exceptionText) {
exceptionMsg.append(text);
}
}
cswException = new CswException(exceptionMsg.toString());
}
if (null == cswException) {
cswException = new CswException(
"Empty Exception Report (version = " + report.getVersion() + ")");
}
return cswException;
}
}