X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blobdiff_plain;f=opendaylight%2Fmd-sal%2Fsal-rest-connector%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fsal%2Frest%2Fimpl%2FRestconfDocumentedExceptionMapper.java;h=10201ab6f5148c78480b9cceea34b77766f0027e;hp=456354bbf0eac9c9eab31c0d72800f5f3a6462e6;hb=414575366e83c6fa254ac1c350bdc7647d5fbc5f;hpb=ec82a960337ba51c2e896863a668dcf8fbcfcb6b diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfDocumentedExceptionMapper.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfDocumentedExceptionMapper.java index 456354bbf0..10201ab6f5 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfDocumentedExceptionMapper.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfDocumentedExceptionMapper.java @@ -8,6 +8,19 @@ package org.opendaylight.controller.sal.rest.impl; +import static org.opendaylight.controller.sal.rest.api.Draft02.RestConfModule.ERRORS_CONTAINER_QNAME; +import static org.opendaylight.controller.sal.rest.api.Draft02.RestConfModule.ERROR_APP_TAG_QNAME; +import static org.opendaylight.controller.sal.rest.api.Draft02.RestConfModule.ERROR_INFO_QNAME; +import static org.opendaylight.controller.sal.rest.api.Draft02.RestConfModule.ERROR_LIST_QNAME; +import static org.opendaylight.controller.sal.rest.api.Draft02.RestConfModule.ERROR_MESSAGE_QNAME; +import static org.opendaylight.controller.sal.rest.api.Draft02.RestConfModule.ERROR_TAG_QNAME; +import static org.opendaylight.controller.sal.rest.api.Draft02.RestConfModule.ERROR_TYPE_QNAME; +import static org.opendaylight.controller.sal.rest.api.Draft02.RestConfModule.NAMESPACE; + +import com.google.common.base.Charsets; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; +import com.google.gson.stream.JsonWriter; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; @@ -15,13 +28,11 @@ import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.util.List; import java.util.Map.Entry; - import javax.activation.UnsupportedDataTypeException; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.Provider; import javax.xml.parsers.DocumentBuilderFactory; @@ -33,9 +44,6 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerFactoryConfigurationError; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; - -import static org.opendaylight.controller.sal.rest.api.Draft02.RestConfModule.*; - import org.opendaylight.controller.sal.restconf.impl.ControllerContext; import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException; import org.opendaylight.controller.sal.restconf.impl.RestconfError; @@ -51,202 +59,186 @@ import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.xml.sax.InputSource; -import com.google.common.base.Strings; -import com.google.common.collect.ImmutableList; -import com.google.gson.stream.JsonWriter; - /** - * This class defines an ExceptionMapper that handles RestconfDocumentedExceptions thrown by - * resource implementations and translates appropriately to restconf error response as defined in - * the RESTCONF RFC draft. + * This class defines an ExceptionMapper that handles RestconfDocumentedExceptions thrown by resource implementations + * and translates appropriately to restconf error response as defined in the RESTCONF RFC draft. * * @author Thomas Pantelis */ @Provider public class RestconfDocumentedExceptionMapper implements ExceptionMapper { - private final static Logger LOG = LoggerFactory.getLogger( RestconfDocumentedExceptionMapper.class ); + private final static Logger LOG = LoggerFactory.getLogger(RestconfDocumentedExceptionMapper.class); @Context private HttpHeaders headers; @Override - public Response toResponse( RestconfDocumentedException exception ) { + public Response toResponse(final RestconfDocumentedException exception) { - LOG.debug( "In toResponse: {}", exception.getMessage() ); + LOG.debug("In toResponse: {}", exception.getMessage()); - // Default to the content type if there's no Accept header - MediaType mediaType = headers.getMediaType(); List accepts = headers.getAcceptableMediaTypes(); + accepts.remove(MediaType.WILDCARD_TYPE); - LOG.debug( "Accept headers: {}", accepts ); + LOG.debug("Accept headers: {}", accepts); - if( accepts != null && accepts.size() > 0 ) { - mediaType = accepts.get( 0 ); // just pick the first one + final MediaType mediaType; + if (accepts != null && accepts.size() > 0) { + mediaType = accepts.get(0); // just pick the first one + } else { + // Default to the content type if there's no Accept header + mediaType = MediaType.APPLICATION_JSON_TYPE; } - LOG.debug( "Using MediaType: {}", mediaType ); + LOG.debug("Using MediaType: {}", mediaType); List errors = exception.getErrors(); - if( errors.isEmpty() ) { + if (errors.isEmpty()) { // We don't actually want to send any content but, if we don't set any content here, // the tomcat front-end will send back an html error report. To prevent that, set a // single space char in the entity. - return Response.status( exception.getStatus() ) - .type( MediaType.TEXT_PLAIN_TYPE ) - .entity( " " ).build(); + return Response.status(exception.getStatus()).type(MediaType.TEXT_PLAIN_TYPE).entity(" ").build(); } - Status status = errors.iterator().next().getErrorTag().getStatusCode(); + int status = errors.iterator().next().getErrorTag().getStatusCode(); ControllerContext context = ControllerContext.getInstance(); - DataNodeContainer errorsSchemaNode = (DataNodeContainer)context.getRestconfModuleErrorsSchemaNode(); + DataNodeContainer errorsSchemaNode = (DataNodeContainer) context.getRestconfModuleErrorsSchemaNode(); - if( errorsSchemaNode == null ) { - return Response.status( status ) - .type( MediaType.TEXT_PLAIN_TYPE ) - .entity( exception.getMessage() ).build(); + if (errorsSchemaNode == null) { + return Response.status(status).type(MediaType.TEXT_PLAIN_TYPE).entity(exception.getMessage()).build(); } ImmutableList.Builder> errorNodes = ImmutableList.> builder(); - for( RestconfError error: errors ) { - errorNodes.add( toDomNode( error ) ); + for (RestconfError error : errors) { + errorNodes.add(toDomNode(error)); } - ImmutableCompositeNode errorsNode = - ImmutableCompositeNode.create( ERRORS_CONTAINER_QNAME, errorNodes.build() ); + ImmutableCompositeNode errorsNode = ImmutableCompositeNode.create(ERRORS_CONTAINER_QNAME, errorNodes.build()); Object responseBody; - if( mediaType.getSubtype().endsWith( "json" ) ) { - responseBody = toJsonResponseBody( errorsNode, errorsSchemaNode ); - } - else { - responseBody = toXMLResponseBody( errorsNode, errorsSchemaNode ); + if (mediaType.getSubtype().endsWith("json")) { + responseBody = toJsonResponseBody(errorsNode, errorsSchemaNode); + } else { + responseBody = toXMLResponseBody(errorsNode, errorsSchemaNode); } - return Response.status( status ).type( mediaType ).entity( responseBody ).build(); + return Response.status(status).type(mediaType).entity(responseBody).build(); } - private Object toJsonResponseBody( ImmutableCompositeNode errorsNode, - DataNodeContainer errorsSchemaNode ) { + private Object toJsonResponseBody(final ImmutableCompositeNode errorsNode, final DataNodeContainer errorsSchemaNode) { - JsonMapper jsonMapper = new JsonMapper(); + JsonMapper jsonMapper = new JsonMapper(null); Object responseBody = null; try { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); - JsonWriter writer = new JsonWriter( new OutputStreamWriter( outStream, "UTF-8" ) ); - writer.setIndent( " " ); + JsonWriter writer = new JsonWriter(new OutputStreamWriter(outStream, Charsets.UTF_8)); + writer.setIndent(" "); - jsonMapper.write( writer, errorsNode, errorsSchemaNode, null ); + jsonMapper.write(writer, errorsNode, errorsSchemaNode); writer.flush(); - responseBody = outStream.toString( "UTF-8" ); - } - catch( IOException e ) { - LOG.error( "Error writing error response body", e ); + responseBody = outStream.toString("UTF-8"); + } catch (IOException e) { + LOG.error("Error writing error response body", e); } return responseBody; } - private Object toXMLResponseBody( ImmutableCompositeNode errorsNode, - DataNodeContainer errorsSchemaNode ) { + private Object toXMLResponseBody(final ImmutableCompositeNode errorsNode, final DataNodeContainer errorsSchemaNode) { XmlMapper xmlMapper = new XmlMapper(); Object responseBody = null; try { - Document xmlDoc = xmlMapper.write( errorsNode, errorsSchemaNode ); + Document xmlDoc = xmlMapper.write(errorsNode, errorsSchemaNode); - responseBody = documentToString( xmlDoc ); - } - catch( TransformerException | UnsupportedDataTypeException | UnsupportedEncodingException e ) { - LOG.error( "Error writing error response body", e ); + responseBody = documentToString(xmlDoc); + } catch (TransformerException | UnsupportedDataTypeException | UnsupportedEncodingException e) { + LOG.error("Error writing error response body", e); } return responseBody; } - private String documentToString( Document doc ) throws TransformerException, UnsupportedEncodingException { + private String documentToString(final Document doc) throws TransformerException, UnsupportedEncodingException { Transformer transformer = createTransformer(); ByteArrayOutputStream outStream = new ByteArrayOutputStream(); - transformer.transform( new DOMSource( doc ), new StreamResult( outStream ) ); + transformer.transform(new DOMSource(doc), new StreamResult(outStream)); - return outStream.toString( "UTF-8" ); + return outStream.toString("UTF-8"); } private Transformer createTransformer() throws TransformerFactoryConfigurationError, - TransformerConfigurationException { + TransformerConfigurationException { TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer = tf.newTransformer(); - transformer.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "no" ); - transformer.setOutputProperty( OutputKeys.METHOD, "xml" ); - transformer.setOutputProperty( OutputKeys.INDENT, "yes" ); - transformer.setOutputProperty( OutputKeys.ENCODING, "UTF-8" ); - transformer.setOutputProperty( "{http://xml.apache.org/xslt}indent-amount", "4" ); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); + transformer.setOutputProperty(OutputKeys.METHOD, "xml"); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); return transformer; } - private Node toDomNode( RestconfError error ) { + private Node toDomNode(final RestconfError error) { CompositeNodeBuilder builder = ImmutableCompositeNode.builder(); - builder.setQName( ERROR_LIST_QNAME ); + builder.setQName(ERROR_LIST_QNAME); - addLeaf( builder, ERROR_TYPE_QNAME, error.getErrorType().getErrorTypeTag() ); - addLeaf( builder, ERROR_TAG_QNAME, error.getErrorTag().getTagValue() ); - addLeaf( builder, ERROR_MESSAGE_QNAME, error.getErrorMessage() ); - addLeaf( builder, ERROR_APP_TAG_QNAME, error.getErrorAppTag() ); + addLeaf(builder, ERROR_TYPE_QNAME, error.getErrorType().getErrorTypeTag()); + addLeaf(builder, ERROR_TAG_QNAME, error.getErrorTag().getTagValue()); + addLeaf(builder, ERROR_MESSAGE_QNAME, error.getErrorMessage()); + addLeaf(builder, ERROR_APP_TAG_QNAME, error.getErrorAppTag()); - Node errorInfoNode = parseErrorInfo( error.getErrorInfo() ); - if( errorInfoNode != null ) { - builder.add( errorInfoNode ); + Node errorInfoNode = parseErrorInfo(error.getErrorInfo()); + if (errorInfoNode != null) { + builder.add(errorInfoNode); } return builder.toInstance(); } - private Node parseErrorInfo( String errorInfo ) { - if( Strings.isNullOrEmpty( errorInfo ) ) { + private Node parseErrorInfo(final String errorInfo) { + if (Strings.isNullOrEmpty(errorInfo)) { return null; } DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware( true ); - factory.setCoalescing( true ); - factory.setIgnoringElementContentWhitespace( true ); - factory.setIgnoringComments( true ); + factory.setNamespaceAware(true); + factory.setCoalescing(true); + factory.setIgnoringElementContentWhitespace(true); + factory.setIgnoringComments(true); // Wrap the error info content in a root element so it can be parsed // as XML. The error info content may or may not be XML. If not then it will be // parsed as text content of the element. - String errorInfoWithRoot = - new StringBuilder( "" ) - .append( errorInfo ).append( "" ).toString(); + String errorInfoWithRoot = new StringBuilder("") + .append(errorInfo).append("").toString(); Document doc = null; try { - doc = factory.newDocumentBuilder().parse( - new InputSource( new StringReader( errorInfoWithRoot ) ) ); - } - catch( Exception e ) { - // TODO: what if the content is text that happens to contain invalid markup? Could - // wrap in CDATA and try again. + doc = factory.newDocumentBuilder().parse(new InputSource(new StringReader(errorInfoWithRoot))); + } catch (Exception e) { + // TODO: what if the content is text that happens to contain invalid markup? + // Could wrap in CDATA and try again. - LOG.warn( "Error parsing restconf error-info, \"" + errorInfo + "\", as XML: " + - e.toString() ); + LOG.warn("Error parsing restconf error-info, \"{}\", as XML", errorInfo, e); return null; } - Node errorInfoNode = XmlDocumentUtils.toDomNode( doc ); + Node errorInfoNode = XmlDocumentUtils.toDomNode(doc); - if( errorInfoNode instanceof CompositeNode ) { - CompositeNode compositeNode = (CompositeNode)XmlDocumentUtils.toDomNode( doc ); + if (errorInfoNode instanceof CompositeNode) { + CompositeNode compositeNode = (CompositeNode) XmlDocumentUtils.toDomNode(doc); // At this point the QName for the "error-info" CompositeNode doesn't contain the revision // as it isn't present in the XML. So we'll copy all the child nodes and create a new @@ -254,20 +246,20 @@ public class RestconfDocumentedExceptionMapper implements ExceptionMapper> childNodes = ImmutableList.builder(); - for( Entry>> entry: compositeNode.entrySet() ) { - childNodes.addAll( entry.getValue() ); + for (Entry>> entry : compositeNode.entrySet()) { + childNodes.addAll(entry.getValue()); } - errorInfoNode = ImmutableCompositeNode.create( ERROR_INFO_QNAME, childNodes.build() ); + errorInfoNode = ImmutableCompositeNode.create(ERROR_INFO_QNAME, childNodes.build()); } return errorInfoNode; } - private void addLeaf( CompositeNodeBuilder builder, QName qname, - String value ) { - if( !Strings.isNullOrEmpty( value ) ) { - builder.addLeaf( qname, value ); + private void addLeaf(final CompositeNodeBuilder builder, final QName qname, + final String value) { + if (!Strings.isNullOrEmpty(value)) { + builder.addLeaf(qname, value); } } }