/*
* Weblounge: Web Content Management System
* Copyright (c) 2003 - 2011 The Weblounge Team
* http://entwinemedia.com/weblounge
*
* This program 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 2
* of the License, or (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package ch.entwine.weblounge.kernel.fop;
import ch.entwine.weblounge.common.site.Environment;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.fop.apps.FOPException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.StreamingOutput;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
/**
* This class implements the <code>REST</code> endpoint for the FOP service.
*/
@Path("/")
@Produces(MediaType.TEXT_XML)
public class FopEndpoint {
/** The loggin facility */
private static final Logger logger = LoggerFactory.getLogger(FopEndpoint.class);
/** The FOP service */
protected transient FopService fopService = null;
/** The request environment */
protected Environment environment = Environment.Production;
/** The endpoint documentation */
private String docs = null;
/**
* Downloads both XML and XSL document and transforms them to PDF.
*
* @param xmlURL
* the URL to the XML document
* @param xslURL
* the URL to the XSL document
* @return the generated PDF
* @throws WebApplicationException
* if the XML document cannot be downloaded
* @throws WebApplicationException
* if the XSL document cannot be downloaded
* @throws WebApplicationException
* if the PDF creation fails
*/
@POST
@Path("/pdf")
public Response transformToPdf(@FormParam("xml") String xmlURL,
@FormParam("xsl") String xslURL, @FormParam("parameters") String params) {
// Make sure we have a service
if (fopService == null)
throw new WebApplicationException(Status.SERVICE_UNAVAILABLE);
final Document xml;
final Document xsl;
// Load the xml document
InputStream xmlInputStream = null;
try {
DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
xmlInputStream = new URL(xmlURL).openStream();
xml = documentBuilder.parse(xmlInputStream);
} catch (MalformedURLException e) {
logger.warn("Error creating xml url from '{}'", xmlURL);
throw new WebApplicationException(Status.BAD_REQUEST);
} catch (IOException e) {
logger.warn("Error accessing xml document at '{}': {}", xmlURL, e.getMessage());
throw new WebApplicationException(Status.NOT_FOUND);
} catch (ParserConfigurationException e) {
logger.warn("Error setting up xml parser: {}", e.getMessage());
throw new WebApplicationException(Status.BAD_REQUEST);
} catch (SAXException e) {
logger.warn("Error parsing xml document from {}: {}", xmlURL, e.getMessage());
throw new WebApplicationException(Status.BAD_REQUEST);
} finally {
IOUtils.closeQuietly(xmlInputStream);
}
// Load the XLST stylesheet
InputStream xslInputStream = null;
try {
DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
xslInputStream = new URL(xslURL).openStream();
xsl = documentBuilder.parse(xslInputStream);
} catch (MalformedURLException e) {
logger.warn("Error creating xsl url from '{}'", xslURL);
throw new WebApplicationException(Status.BAD_REQUEST);
} catch (IOException e) {
logger.warn("Error accessing xsl stylesheet at '{}': {}", xslURL, e.getMessage());
throw new WebApplicationException(Status.NOT_FOUND);
} catch (ParserConfigurationException e) {
logger.warn("Error setting up xml parser: {}", e.getMessage());
throw new WebApplicationException(Status.BAD_REQUEST);
} catch (SAXException e) {
logger.warn("Error parsing xml document from {}: {}", xslURL, e.getMessage());
throw new WebApplicationException(Status.BAD_REQUEST);
} finally {
IOUtils.closeQuietly(xslInputStream);
}
// Create the filename
String name = FilenameUtils.getBaseName(xmlURL) + ".pdf";
// Process the parameters
final List<String[]> parameters = new ArrayList<String[]>();
if (StringUtils.isNotBlank(params)) {
for (String param : StringUtils.split(params, ";")) {
String[] parameterValue = StringUtils.split(param, "=");
if (parameterValue.length != 2) {
logger.warn("Parameter for PDF generation is malformed: {}", param);
throw new WebApplicationException(Status.BAD_REQUEST);
}
parameters.add(new String[] {
StringUtils.trim(parameterValue[0]),
StringUtils.trim(parameterValue[1]) });
}
}
// Write the file contents back
ResponseBuilder response = Response.ok(new StreamingOutput() {
public void write(OutputStream os) throws IOException,
WebApplicationException {
try {
fopService.xml2pdf(xml, xsl, parameters.toArray(new String[parameters.size()][2]), os);
} catch (IOException e) {
Throwable cause = e.getCause();
if (cause == null || !"Broken pipe".equals(cause.getMessage()))
logger.warn("Error writing file contents to response", e);
} catch (TransformerConfigurationException e) {
logger.error("Error setting up the XSL transfomer: {}", e.getMessage());
throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
} catch (FOPException e) {
logger.error("Error creating PDF document: {}", e.getMessage());
throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
} catch (TransformerException e) {
logger.error("Error transforming to PDF: {}", e.getMessage());
throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
} finally {
IOUtils.closeQuietly(os);
}
}
});
// Set response information
response.type("application/pdf");
response.header("Content-Disposition", "inline; filename=" + name);
response.lastModified(new Date());
return response.build();
}
/**
* Returns the endpoint documentation.
*
* @return the endpoint documentation
*/
@GET
@Path("/docs")
@Produces(MediaType.TEXT_HTML)
public String getDocumentation(@Context HttpServletRequest request) {
if (docs == null) {
String docsPath = request.getRequestURI();
String docsPathExtension = request.getPathInfo();
String servicePath = request.getRequestURI().substring(0, docsPath.length() - docsPathExtension.length());
docs = FopEndpointDocs.createDocumentation(servicePath);
}
return docs;
}
/**
* Callback for OSGi to set the FOP service.
*
* @param fopService
* the FOP service
*/
void setFopService(FopService fopService) {
this.fopService = fopService;
}
/**
* Callback for OSGi to remove the FOP service.
*
* @param fopService
* the FOP service
*/
void removeFopService(FopService fopService) {
this.fopService = null;
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Fop rest endpoint";
}
}