Juan Hernandez has uploaded a new change for review.

Change subject: restapi: Improve exception handling in FOP writer
......................................................................

restapi: Improve exception handling in FOP writer

This is basically to make Coverity happy.

Change-Id: I6d3202ba59ebe111f0180956a839e46b57149506
Signed-off-by: Juan Hernandez <[email protected]>
---
M 
backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/pdf/FOPMessageBodyWriter.java
A backend/manager/modules/restapi/jaxrs/src/main/resources/pdf/Cluster.xsl
R 
backend/manager/modules/restapi/jaxrs/src/main/resources/pdf/GlusterVolumeProfileDetails.xsl
3 files changed, 163 insertions(+), 44 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/44/40344/1

diff --git 
a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/pdf/FOPMessageBodyWriter.java
 
b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/pdf/FOPMessageBodyWriter.java
index 1749393..60bca35 100644
--- 
a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/pdf/FOPMessageBodyWriter.java
+++ 
b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/pdf/FOPMessageBodyWriter.java
@@ -1,11 +1,30 @@
+/*
+* 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.pdf;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
 
 import javax.ws.rs.Produces;
 import javax.ws.rs.WebApplicationException;
@@ -15,45 +34,94 @@
 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.util.JAXBSource;
 import javax.xml.transform.Result;
 import javax.xml.transform.Source;
 import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
 import javax.xml.transform.TransformerFactory;
 import javax.xml.transform.sax.SAXResult;
 import javax.xml.transform.stream.StreamSource;
 
+import org.apache.fop.apps.FOPException;
 import org.ovirt.engine.api.model.ObjectFactory;
 import org.ovirt.engine.api.model.API;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.apache.fop.apps.FOUserAgent;
 import org.apache.fop.apps.Fop;
 import org.apache.fop.apps.FopFactory;
 import org.apache.fop.apps.MimeConstants;
+import org.ovirt.engine.api.resource.ApiMediaType;
 
+/**
+ * This writer generates PDF documents from model objects transforming them 
first into XML, then into FO (Formatting
+ * Objects) using an XSLT transformation, and then into actual PDF using FOP.
+ */
 @Provider
-@Produces("application/pdf")
+@Produces(ApiMediaType.APPLICATION_PDF)
 public class FOPMessageBodyWriter implements MessageBodyWriter<Object> {
+    /**
+     * The factory used to create JAXB elements.
+     */
+    private ObjectFactory objectFactory = new ObjectFactory();
 
-    private static final Logger log = 
LoggerFactory.getLogger(FOPMessageBodyWriter.class);
+    /**
+     * A index used to speed up finding the factory method used to create JAXB 
elements.
+     */
+    private Map<Class<?>, Method> factoryMethods = new HashMap<>();
+
+    /**
+     * The JAXB context used to convert XML documents into the corresponding 
model objects.
+     */
     private JAXBContext jaxbContext;
-    private TransformerFactory transfact;
+
+    /**
+     * The factory used to create XSLT transformers.
+     */
+    private TransformerFactory transformerFactory = 
TransformerFactory.newInstance();
+
+    /**
+     * The factory used to create FOP objects.
+     */
     private FopFactory fopFactory;
+
+    /**
+     * The FO user agent.
+     */
     private FOUserAgent foUserAgent;
-    private ObjectFactory objectFactory;
 
     public FOPMessageBodyWriter() {
-        try {
-            String modelPackage = API.class.getPackage().getName();
-            jaxbContext = JAXBContext.newInstance(modelPackage);
-            transfact = TransformerFactory.newInstance();
-            fopFactory = FopFactory.newInstance();
-            foUserAgent = fopFactory.newFOUserAgent();
-            objectFactory = new ObjectFactory();
-        } catch (Exception error) {
-            log.error("Error while creating FOP message body writer.", error);
+        // In order to create the JAXB element that wraps the object we need 
to call the method of the object factory
+        // that uses the correct element name, and in order to avoid doing 
this with every request we populate this
+        // map in advance:
+        for (Method factoryMethod : ObjectFactory.class.getDeclaredMethods()) {
+            Class<?>[] parameterTypes = factoryMethod.getParameterTypes();
+            if (parameterTypes.length == 1) {
+                factoryMethods.put(parameterTypes[0], factoryMethod);
+            }
         }
+
+        // Create a JAXB context for the model package:
+        String modelPackage = API.class.getPackage().getName();
+        try {
+            jaxbContext = JAXBContext.newInstance(modelPackage);
+        }
+        catch (JAXBException exception) {
+            throw new IllegalStateException(
+                "Can't create JAXB context for package \"" + modelPackage + 
"\".",
+                exception
+            );
+        }
+
+        // Create the XSLT transformer factory:
+        transformerFactory = TransformerFactory.newInstance();
+
+        // Create the FOP factory:
+        fopFactory = FopFactory.newInstance();
+
+        // Create the FO user agent:
+        foUserAgent = fopFactory.newFOUserAgent();
     }
 
     @Override
@@ -62,41 +130,65 @@
     }
 
     @Override
-    public boolean isWriteable(Class<?> dataClass, Type arg1, Annotation[] 
annotations, MediaType mediaType) {
+    public boolean isWriteable(Class<?> dataClass, Type genericType, 
Annotation[] annotations, MediaType mediaType) {
         return true;
     }
 
     @Override
-    public void writeTo(final Object data, Class<?> type, Type genericType, 
Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> 
httpHeaders, OutputStream entityStream) throws IOException, 
WebApplicationException {
-        String xslName = "/" + type.getSimpleName() + "AsPdf.xsl";
-        try (InputStream templateStream = type.getResourceAsStream(xslName)) {
+    public void writeTo(Object object, Class<?> type, Type genericType, 
Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, Object> httpHeaders, OutputStream 
entityStream)
+            throws IOException, WebApplicationException {
+        // Locate and load the XSLT template:
+        String templateName = "/pdf/" + type.getSimpleName() + ".xsl";
+        Transformer template;
+        try (InputStream templateStream = 
FOPMessageBodyWriter.class.getResourceAsStream(templateName)) {
             if (templateStream != null) {
-                StreamSource transformSource = new 
StreamSource(templateStream);
-
-                Method factoryMethod = null;
-                for (Method currentMethod : 
objectFactory.getClass().getDeclaredMethods()) {
-                    Class<?>[] parameterTypes = 
currentMethod.getParameterTypes();
-                    if (parameterTypes.length == 1 && parameterTypes[0] == 
type) {
-                        factoryMethod = currentMethod;
-                        break;
-                    }
-                }
-                if (data != null && factoryMethod != null) {
-                    JAXBElement<?> element = (JAXBElement<?>) 
factoryMethod.invoke(objectFactory, data);
-                    Source source = new JAXBSource(jaxbContext, element);
-
-                    Transformer xslfoTransformer = 
transfact.newTransformer(transformSource);
-                    Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, 
foUserAgent, entityStream);
-                    Result res = new SAXResult(fop.getDefaultHandler());
-                    xslfoTransformer.transform(source, res);
-                } else {
-                    log.error("Data not available");
-                }
-            } else {
-                log.error("Error while generating PDF. Null InputStream");
+                StreamSource templateSource = new StreamSource(templateStream);
+                template = transformerFactory.newTransformer(templateSource);
             }
-        } catch (Exception e) {
-            log.error("Error while generating PDF. ", e);
+            else {
+                throw new IOException("Can't find resource for XSLT template 
\"" + templateName + "\".");
+            }
+        }
+        catch (TransformerConfigurationException exception) {
+            throw new IOException("Can't load XSLT template \"" + templateName 
+ "\".", exception);
+        }
+
+        // Find the factory method used to create the JAXB element with the 
right tag:
+        Method factoryMethod = factoryMethods.get(type);
+        if (factoryMethod == null) {
+            throw new IOException("Can't find factory method for type \"" + 
type.getName() + "\".");
+        }
+
+        // Invoke the method to create the JAXB element:
+        JAXBElement<Object> element;
+        try {
+            element = (JAXBElement<Object>) 
factoryMethod.invoke(objectFactory, object);
+        }
+        catch (IllegalAccessException | InvocationTargetException exception) {
+            throw new IOException("Error invoking factory method for type \"" 
+ type.getName() + "\".", exception);
+        }
+
+        // Wrap the created JAXB element with an object that can be used as 
the source of the XSLT transformation:
+        Source source;
+        try {
+            source = new JAXBSource(jaxbContext, element);
+        }
+        catch (JAXBException exception) {
+            throw new IOException("Can't create transformation source from 
JAXB element.", exception);
+        }
+
+        // Run the XSLT transformation using the JAXB element as source and 
the entity stream as result:
+        try {
+            Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, 
entityStream);
+            Result result = new SAXResult(fop.getDefaultHandler());
+            template.transform(source, result);
+        }
+        catch (TransformerException | FOPException exception) {
+            throw new IOException(
+                "Error while generating PDF document using XSLT template \"" + 
templateName + "\".",
+                exception
+            );
         }
     }
 }
diff --git 
a/backend/manager/modules/restapi/jaxrs/src/main/resources/pdf/Cluster.xsl 
b/backend/manager/modules/restapi/jaxrs/src/main/resources/pdf/Cluster.xsl
new file mode 100644
index 0000000..7a7cd8d
--- /dev/null
+++ b/backend/manager/modules/restapi/jaxrs/src/main/resources/pdf/Cluster.xsl
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<xsl:stylesheet
+  version="1.0"
+  xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
+  xmlns:fo="http://www.w3.org/1999/XSL/Format";>
+
+  <xsl:output method="xml" indent="yes"/>
+
+  <xsl:template match="/">
+    <fo:root>
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="A4-portrait" page-height="29.7cm" 
page-width="21.0cm" margin="2cm">
+          <fo:region-body/>
+        </fo:simple-page-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="A4-portrait">
+        <fo:flow flow-name="xsl-region-body">
+          <fo:block>
+            Hello!
+          </fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git 
a/backend/manager/modules/restapi/jaxrs/src/main/resources/GlusterVolumeProfileDetailsAsPdf.xsl
 
b/backend/manager/modules/restapi/jaxrs/src/main/resources/pdf/GlusterVolumeProfileDetails.xsl
similarity index 100%
rename from 
backend/manager/modules/restapi/jaxrs/src/main/resources/GlusterVolumeProfileDetailsAsPdf.xsl
rename to 
backend/manager/modules/restapi/jaxrs/src/main/resources/pdf/GlusterVolumeProfileDetails.xsl


-- 
To view, visit https://gerrit.ovirt.org/40344
To unsubscribe, visit https://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I6d3202ba59ebe111f0180956a839e46b57149506
Gerrit-PatchSet: 1
Gerrit-Project: ovirt-engine
Gerrit-Branch: master
Gerrit-Owner: Juan Hernandez <[email protected]>
_______________________________________________
Engine-patches mailing list
[email protected]
http://lists.ovirt.org/mailman/listinfo/engine-patches

Reply via email to