Juan Hernandez has uploaded a new change for review.

Change subject: restapi: IO exception mapper
......................................................................

restapi: IO exception mapper

This patch introduces a new message body reader that generates always an
IO exception, containing the cause of the error, and a generic exception
mapper that takes that IO exception, extracts the cause and generates
the corresponding message.

Change-Id: Id63e6eee673723ac32e3c2740f338f7ca465c6ac
Bug-Url: https://bugzilla.redhat.com/1110295
Signed-off-by: Juan Hernandez <juan.hernan...@redhat.com>
(cherry picked from commit a47e95e9d5eed10a57ef38963450421ccd32a07f)
---
M backend/manager/modules/restapi/interface/definition/pom.xml
A 
backend/manager/modules/restapi/interface/definition/src/main/java/org/ovirt/engine/api/xml/JAXBMessageBodyReader.java
A 
backend/manager/modules/restapi/interface/definition/src/main/java/org/ovirt/engine/api/xml/JAXBValidationEventHandler.java
M 
backend/manager/modules/restapi/interface/definition/src/main/resources/META-INF/services/javax.ws.rs.ext.Providers
M 
backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/BackendApplication.java
A 
backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/validation/IOExceptionMapper.java
D 
backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/validation/InvalidValueExceptionMapper.java
D 
backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/validation/XmlMessageBodyReader.java
M backend/manager/modules/restapi/webapp/src/main/webapp/WEB-INF/web.xml
9 files changed, 264 insertions(+), 99 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/47/28847/1

diff --git a/backend/manager/modules/restapi/interface/definition/pom.xml 
b/backend/manager/modules/restapi/interface/definition/pom.xml
index 578d8bd..a03802e 100644
--- a/backend/manager/modules/restapi/interface/definition/pom.xml
+++ b/backend/manager/modules/restapi/interface/definition/pom.xml
@@ -52,6 +52,12 @@
       <groupId>commons-lang</groupId>
       <artifactId>commons-lang</artifactId>
     </dependency>
+
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+
   </dependencies>
 
   <build>
diff --git 
a/backend/manager/modules/restapi/interface/definition/src/main/java/org/ovirt/engine/api/xml/JAXBMessageBodyReader.java
 
b/backend/manager/modules/restapi/interface/definition/src/main/java/org/ovirt/engine/api/xml/JAXBMessageBodyReader.java
new file mode 100644
index 0000000..922f401
--- /dev/null
+++ 
b/backend/manager/modules/restapi/interface/definition/src/main/java/org/ovirt/engine/api/xml/JAXBMessageBodyReader.java
@@ -0,0 +1,147 @@
+/*
+* Copyright (c) 2014 Red Hat, Inc.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package org.ovirt.engine.api.xml;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+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.ext.MessageBodyReader;
+import javax.ws.rs.ext.Provider;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.bind.ValidationEventHandler;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+import org.ovirt.engine.api.model.API;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class is responsible for converting XML documents into model objects. 
Note that it can't be a generic class
+ * because if it is then the JAX-RS framework will select other builtin 
classes that are more specific.
+ */
+@Provider
+@Consumes(MediaType.APPLICATION_XML)
+public class JAXBMessageBodyReader implements MessageBodyReader<Object> {
+    /**
+     * The logger used by this class.
+     */
+    private Logger log = LoggerFactory.getLogger(JAXBMessageBodyReader.class);
+
+    /**
+     * The factory used to create XML document readers.
+     */
+    private XMLInputFactory parserFactory;
+
+    /**
+     * The JAXB jaxbContext used to convert XML documents into the 
corresponding model objects.
+     */
+    private JAXBContext jaxbContext;
+
+    /**
+     * Default event handler recognizes XML parsing as error and not as 
warning.
+     */
+    private ValidationEventHandler jaxbHandler = new 
JAXBValidationEventHandler();
+
+    public JAXBMessageBodyReader() {
+        // Create a factory that will produce XML parsers that ignore entity 
references and DTDs:
+        parserFactory = XMLInputFactory.newFactory();
+        
parserFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, 
false);
+        
parserFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, 
false);
+        parserFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
+
+        // Create a JAXB context for the model package:
+        String modelPackage = API.class.getPackage().getName();
+        try {
+            jaxbContext = JAXBContext.newInstance(modelPackage);
+        }
+        catch (JAXBException exception) {
+            log.error("Can't create JAXB context for package \"{}\".", 
modelPackage, exception);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isReadable(Class<?> type, Type genericType, Annotation 
annotations[], MediaType mediaType) {
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Object readFrom(Class<Object> type, Type genericType, Annotation 
annotations[], MediaType mediaType,
+            MultivaluedMap<String, String> httpHeaders, InputStream 
entityStream)
+            throws IOException, WebApplicationException {
+        XMLStreamReader reader = null;
+        try {
+            reader = parserFactory.createXMLStreamReader(entityStream, 
"UTF-8");
+            return readFrom(reader);
+        }
+        catch (XMLStreamException exception) {
+            throw new IOException(exception);
+        }
+        finally {
+            if (reader != null) {
+                try {
+                    reader.close();
+                }
+                catch (XMLStreamException exception) {
+                    log.warn("Can't close XML stream reader.", exception);
+                }
+            }
+        }
+    }
+
+    /**
+     * Read the XML document using the given reader and convert it to an 
object. The given reader will be closed by the
+     * caller.
+     */
+    private Object readFrom(XMLStreamReader reader) throws IOException {
+        try {
+            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
+            unmarshaller.setEventHandler(jaxbHandler);
+            Object result = unmarshaller.unmarshal(reader);
+            if (result instanceof JAXBElement) {
+                result = ((JAXBElement) result).getValue();
+            }
+            return result;
+        }
+        catch (JAXBException exception) {
+            Throwable linked = exception.getLinkedException();
+            if (linked != null) {
+                Throwable cause = linked;
+                while (cause.getCause() != null) {
+                    cause = cause.getCause();
+                }
+                throw new IOException(cause);
+            }
+            throw new IOException(exception);
+        }
+    }
+}
diff --git 
a/backend/manager/modules/restapi/interface/definition/src/main/java/org/ovirt/engine/api/xml/JAXBValidationEventHandler.java
 
b/backend/manager/modules/restapi/interface/definition/src/main/java/org/ovirt/engine/api/xml/JAXBValidationEventHandler.java
new file mode 100644
index 0000000..8c55f5e
--- /dev/null
+++ 
b/backend/manager/modules/restapi/interface/definition/src/main/java/org/ovirt/engine/api/xml/JAXBValidationEventHandler.java
@@ -0,0 +1,38 @@
+/*
+* Copyright (c) 2014 Red Hat, Inc.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package org.ovirt.engine.api.xml;
+
+import javax.xml.bind.ValidationEvent;
+import javax.xml.bind.ValidationEventHandler;
+
+/**
+ * This validation event handler considers all errors as fatal, and reports 
them so that the JAXB context will abort the
+ * operation and throw an exception. If this isn't used, type errors (integer 
overflows, for example) are silently
+ * ignored.
+ */
+public class JAXBValidationEventHandler implements ValidationEventHandler {
+    @Override
+    public boolean handleEvent(ValidationEvent event) {
+        switch (event.getSeverity()) {
+        case ValidationEvent.ERROR:
+        case ValidationEvent.FATAL_ERROR:
+            return false;
+        default:
+            return true;
+        }
+    }
+}
diff --git 
a/backend/manager/modules/restapi/interface/definition/src/main/resources/META-INF/services/javax.ws.rs.ext.Providers
 
b/backend/manager/modules/restapi/interface/definition/src/main/resources/META-INF/services/javax.ws.rs.ext.Providers
index 3d2b90c..377bfa1 100644
--- 
a/backend/manager/modules/restapi/interface/definition/src/main/resources/META-INF/services/javax.ws.rs.ext.Providers
+++ 
b/backend/manager/modules/restapi/interface/definition/src/main/resources/META-INF/services/javax.ws.rs.ext.Providers
@@ -1 +1,2 @@
 org.ovirt.engine.api.resteasy.json.JsonProvider
+org.ovirt.engine.api.xml.JAXBMessageBodyReader
\ No newline at end of file
diff --git 
a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/BackendApplication.java
 
b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/BackendApplication.java
index 5b7d9e1..d556c9b 100644
--- 
a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/BackendApplication.java
+++ 
b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/BackendApplication.java
@@ -58,13 +58,12 @@
 import org.ovirt.engine.api.restapi.resource.BackendVmPoolsResource;
 import org.ovirt.engine.api.restapi.resource.BackendVmsResource;
 import org.ovirt.engine.api.restapi.resource.BackendVnicProfilesResource;
-import 
org.ovirt.engine.api.restapi.resource.validation.InvalidValueExceptionMapper;
+import org.ovirt.engine.api.restapi.resource.validation.IOExceptionMapper;
 import org.ovirt.engine.api.restapi.resource.validation.JaxbExceptionMapper;
 import 
org.ovirt.engine.api.restapi.resource.validation.JaxbMarshallExceptionMapper;
 import org.ovirt.engine.api.restapi.resource.validation.JsonExceptionMapper;
 import 
org.ovirt.engine.api.restapi.resource.validation.MalformedIdExceptionMapper;
 import org.ovirt.engine.api.restapi.resource.validation.ValidatorLocator;
-import org.ovirt.engine.api.restapi.resource.validation.XmlMessageBodyReader;
 import org.ovirt.engine.api.restapi.security.auth.LoginValidator;
 import org.ovirt.engine.api.restapi.types.MappingLocator;
 import org.ovirt.engine.api.restapi.util.SessionHelper;
@@ -165,15 +164,12 @@
         singletons.add(new ResponseStatusLogger());
         singletons.add(new ResponsePayloadLogger());
 
-        // JAXB message body reader that sets default validation event handler
-        singletons.add(new XmlMessageBodyReader());
-
         // Intercepter that maps exceptions cause by illegal guid string to 
400 status (BAD_REQUEST).
         singletons.add(new MalformedIdExceptionMapper());
         singletons.add(new JaxbExceptionMapper());
         singletons.add(new JaxbMarshallExceptionMapper());
         singletons.add(new JsonExceptionMapper());
-        singletons.add(new InvalidValueExceptionMapper());
+        singletons.add(new IOExceptionMapper());
     }
 
     private void addResource(final BackendResource resource) {
diff --git 
a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/validation/IOExceptionMapper.java
 
b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/validation/IOExceptionMapper.java
new file mode 100644
index 0000000..d282864
--- /dev/null
+++ 
b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/validation/IOExceptionMapper.java
@@ -0,0 +1,70 @@
+/*
+* Copyright (c) 2014 Red Hat, Inc.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package org.ovirt.engine.api.restapi.resource.validation;
+
+import java.io.IOException;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.ovirt.engine.api.model.Fault;
+import org.ovirt.engine.api.model.UsageMessage;
+import org.ovirt.engine.api.utils.InvalidValueException;
+
+/**
+ * This mapper is intended to handle the exceptions thrown by the JAXB message 
body reader.
+ */
+@Provider
+public class IOExceptionMapper implements ExceptionMapper<IOException> {
+
+    @Context
+    protected UriInfo uriInfo;
+    @Context
+    protected Request request;
+    @Context
+    protected Application application;
+
+    @Override
+    public Response toResponse(IOException exception) {
+        // Check if the cause of the exception is an invalid value, and 
generate a specific error message:
+        Throwable cause = exception.getCause();
+        if (cause instanceof InvalidValueException) {
+            Fault fault = new Fault();
+            fault.setReason("Invalid value");
+            fault.setDetail(cause.getMessage());
+            return 
Response.status(Response.Status.BAD_REQUEST).entity(fault).build();
+        }
+
+        // For any other kind of exception generate an error response with 
information describing the correct syntax for
+        // the request:
+        try {
+            UsageFinder finder = new UsageFinder();
+            UsageMessage usage = finder.getUsageMessage(application, uriInfo, 
request);
+            return Response.status(Status.BAD_REQUEST).entity(usage).build();
+        }
+        catch (Exception error) {
+            throw new WebApplicationException(error, 
Response.status(Status.INTERNAL_SERVER_ERROR).build());
+        }
+    }
+
+}
diff --git 
a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/validation/InvalidValueExceptionMapper.java
 
b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/validation/InvalidValueExceptionMapper.java
deleted file mode 100644
index 7b328ed..0000000
--- 
a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/validation/InvalidValueExceptionMapper.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package org.ovirt.engine.api.restapi.resource.validation;
-
-import org.ovirt.engine.api.model.Fault;
-import org.ovirt.engine.api.utils.InvalidValueException;
-
-import javax.ws.rs.core.Response;
-import javax.ws.rs.ext.ExceptionMapper;
-import javax.ws.rs.ext.Provider;
-
-@Provider
-public class InvalidValueExceptionMapper implements 
ExceptionMapper<InvalidValueException> {
-
-    @Override
-    public Response toResponse(InvalidValueException e) {
-        Fault fault = new Fault();
-        fault.setReason("Invalid Value");
-        fault.setDetail(e.getMessage());
-        return 
Response.status(Response.Status.BAD_REQUEST).entity(fault).build();
-    }
-}
diff --git 
a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/validation/XmlMessageBodyReader.java
 
b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/validation/XmlMessageBodyReader.java
deleted file mode 100644
index 9d05f98..0000000
--- 
a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/validation/XmlMessageBodyReader.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package org.ovirt.engine.api.restapi.resource.validation;
-
-import org.jboss.resteasy.plugins.providers.jaxb.AbstractJAXBProvider;
-import org.jboss.resteasy.plugins.providers.jaxb.JAXBMarshalException;
-import org.ovirt.engine.api.utils.InvalidValueException;
-
-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.ext.Provider;
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBElement;
-import javax.xml.bind.JAXBException;
-import javax.xml.bind.Unmarshaller;
-import javax.xml.bind.ValidationEventHandler;
-import javax.xml.bind.helpers.DefaultValidationEventHandler;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Type;
-
-@Provider
-@Consumes({ MediaType.APPLICATION_XML })
-public class XmlMessageBodyReader extends AbstractJAXBProvider<Object> {
-
-    /** Default event handler recognizes XML parsing as error and not as 
warning */
-    private ValidationEventHandler errorhandler = new 
DefaultValidationEventHandler();
-
-    @Override
-    protected boolean isReadWritable(Class<?> type, Type genericType, 
Annotation[] annotations, MediaType mediaType) {
-        return true;
-    }
-
-    /**
-     * {@inheritDoc}
-     * <p>
-     * Standard JAXB unmarshaller with custom error handler- {@link 
#errorhandler}
-     */
-    @Override
-    public Object readFrom(Class<Object> type, Type genericType, Annotation[] 
annotations, MediaType mediaType,
-                           MultivaluedMap<String, String> httpHeaders, 
InputStream entityStream) throws IOException, WebApplicationException {
-
-        if (entityStream == null) {
-            return null;
-        }
-        try {
-            JAXBContext ctx = findJAXBContext(type, annotations, mediaType, 
true);
-            Unmarshaller unmarshaller = ctx.createUnmarshaller();
-
-            AbstractJAXBProvider.decorateUnmarshaller(type, annotations, 
mediaType, unmarshaller);
-
-            unmarshaller.setEventHandler(errorhandler);
-            Object parsedObj = unmarshaller.unmarshal(entityStream);
-            if (parsedObj instanceof JAXBElement) {
-                return ((JAXBElement) parsedObj).getValue();
-            }
-            return parsedObj;
-        } catch (JAXBException e) {
-            if (e.getLinkedException().getCause() instanceof 
InvalidValueException) {
-                throw (InvalidValueException) 
e.getLinkedException().getCause();
-            }
-            throw new JAXBMarshalException(e);
-        }
-    }
-
-}
diff --git 
a/backend/manager/modules/restapi/webapp/src/main/webapp/WEB-INF/web.xml 
b/backend/manager/modules/restapi/webapp/src/main/webapp/WEB-INF/web.xml
index b1f1606..44927e0 100644
--- a/backend/manager/modules/restapi/webapp/src/main/webapp/WEB-INF/web.xml
+++ b/backend/manager/modules/restapi/webapp/src/main/webapp/WEB-INF/web.xml
@@ -6,12 +6,6 @@
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd";
   version="3.0">
 
-  <!-- Disable entity expansion in RESTEasy: -->
-  <context-param>
-    <param-name>resteasy.document.expand.entity.references</param-name>
-    <param-value>false</param-value>
-  </context-param>
-
   <!-- Perform authentication: -->
   <filter>
     <filter-name>AuthenticationFilter</filter-name>


-- 
To view, visit http://gerrit.ovirt.org/28847
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Id63e6eee673723ac32e3c2740f338f7ca465c6ac
Gerrit-PatchSet: 1
Gerrit-Project: ovirt-engine
Gerrit-Branch: ovirt-engine-3.4
Gerrit-Owner: Juan Hernandez <juan.hernan...@redhat.com>
_______________________________________________
Engine-patches mailing list
Engine-patches@ovirt.org
http://lists.ovirt.org/mailman/listinfo/engine-patches

Reply via email to