Fun with REST, Spring 3 and Jasper

Hey folks, in this post i will show how to have fun coding Web services with REST, Spring 3 and Jasper Reports. First of all i will give you some background about REST. We gonna code our Web Service REST using Apache CXF. You can work the old way, with CXF using the standards like ws-* but here we will use REST only.

Why REST?


REST(Representational State Transfer) is not a standard. It is an Architectural Style defined in 2000 by Roy Fielding. The Style basically consist in leverage http method like PUT, DELETE and not be stuck only to POST and GET. With REST your are not limited to XML, you have freedom to work with different media types(data format) like Json, Protobuf, binary, raw text or whatever media-type you want even xml.

REST is simply. I don't know if it will remain for long time but pray all day for that happen :-). There are a couple of enemies trying to transform REST in something more look like ws-*, such wadl(very nasty). I know you guys want some way to import your REST web services metadata into soapui or some SOA governance system, but i must say that this is really a bad idea.

Any programming language capable with an http api can use REST and this is an great advantage not only because the enterprise world can have advantage of that fact but also because the mobile and mash-up world and this is an big deal.

Why Spring Framework 3.0 ?

Spring Framework is a "De Facto" standard solution for IoC and DI for Java, Python and .NET solution. Better design and maintenance are not the only reasons making me use Spring Framework 3.0 but also an consistent simple programming model.


In other post i will show how to use Spring Framework 3.0 as your REST provider, for while lets use CXF for that job and focus on spring as di/ioc :-)

Whats is the deal?

Now, i will show how to generate and PDF report using Jasper and return that PDF file via REST. So its time to code! If you don't have patience and what check out the code to start hacking, go ahead np man. You can checkout the source code here on my subversion: http://diegopacheco.svn.beanstalkapp.com/sandbox/trunk/rest-spring3-jasper/

JasperRestService.java
<br>package com.blogspot.diegopacheco.rest.report;<br><br>import javax.ws.rs.GET;<br>import javax.ws.rs.Path;<br>import javax.ws.rs.PathParam;<br>import javax.ws.rs.Produces;<br>import javax.ws.rs.core.Response;<br><br>/**<br> * <br> * JasperRestService<br> *<br> * @author Diego Pacheco<br> * @since  01/09/2010<br> * @version 1.0<br> *<br> */<br>public interface JasperRestService {<br>    <br>    @GET<br>    @Path("/pdf/{tittle}/{owner}/")    <br>    @Produces({"application/pdf"})<br>    public Response doPdfReport(@PathParam("tittle") String tittle,@PathParam("owner") String owner);<br>    <br>}<br>

We have a couple of annotations here, so let me explain all those for you.

@Get: This annotation represent the GET http method, the good news is that you can test this operation(doPdfReport) using your favorite web browser.

@Path: We use this annotation to map the url path for this operation(doPdfReport). The first string is the path but the third and the fourth are parameters for the java method.

@Produces: Here you configure the media type, in other words it is the format of your returning data, in this case is an PDF file generated by jasper reports.

@PathParam: Responsible for bind the parameter described in the @Path with the java method parameter.

You may realized that I'm returning an javax.ws.rs.core.Response rather than byte[], its is possible return an byte[] but if i do that i will not be able to define the report file name.


JasperRestServiceImpl.java
</p><p>package com.blogspot.diegopacheco.rest.report;<br><br>import java.io.ByteArrayOutputStream;<br>import java.io.InputStream;<br>import java.util.HashMap;<br><br>import javax.ws.rs.core.Response;<br><br>import net.sf.jasperreports.engine.JREmptyDataSource;<br>import net.sf.jasperreports.engine.JRExporter;<br>import net.sf.jasperreports.engine.JRExporterParameter;<br>import net.sf.jasperreports.engine.JasperFillManager;<br>import net.sf.jasperreports.engine.JasperPrint;<br>import net.sf.jasperreports.engine.export.JRPdfExporter;<br><br><br>/**<br> * <br> * JasperRestService<br> *<br> * @author Diego Pacheco<br> * @since  01/09/2010<br> * @version 1.0<br> *<br> */<br>public class JasperRestServiceImpl implements JasperRestService{<br><br>    @Override<br>    public Response doPdfReport(String tittle,String owner){<br>        try{<br>            <br>            InputStream is = this.getClass().getClassLoader().getResourceAsStream("reports/LivrosReport.jasper");<br>            JREmptyDataSource emptyDataSource = new JREmptyDataSource();<br>            <br>            HashMap<String, Object> parameters = new HashMap<String, Object>();<br>            parameters.put("tittle", tittle);<br>            parameters.put("owner", owner);<br>            <br>            JasperPrint jasperPrint = JasperFillManager.fillReport(is, parameters, emptyDataSource);<br>            <br>            ByteArrayOutputStream baos = new ByteArrayOutputStream();            <br>            JRExporter exporter = new JRPdfExporter();<br>            <br>            exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);<br>            exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, baos);<br>            exporter.exportReport();<br>            <br>            byte[] result =  baos.toByteArray();<br>            return Response.<br>                    ok(result).<br>                    header("Content-Disposition", "inline; filename=spring3-rest-jasper-report.pdf").<br>                    build();    <br>            <br>        }catch(Exception e){<br>            throw new RuntimeException(e);<br>        }<br>        <br>    }<br>    <br>}<br>

In this code showed above i just pass the tittle and owner parameters to jasper(this parameters will be used in the report). You can notice that Response object is an nice DSL and I'm setting the http header "Content-Disposition" in order to provide the report file name.

Now, let see the spring configuration file:

spring-cxf-beans.xml
<br><?xml version="1.0" encoding="UTF-8"?><br><beans xmlns="http://www.springframework.org/schema/beans"<br>    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cxf="http://cxf.apache.org/core"<br>    xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:jaxrs="http://cxf.apache.org/jaxrs"<br>    xmlns:soap="http://cxf.apache.org/bindings/soap"<br>    xsi:schemaLocation="<br>         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd<br>         http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd<br>         http://cxf.apache.org/bindings/soap http://cxf.apache.org/schemas/configuration/soap.xsd<br>            http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd<br>         http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd"><br><br>    <import resource="classpath:META-INF/cxf/cxf.xml" /><br>    <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" /><br>    <import resource="classpath:META-INF/cxf/cxf-extension-jaxrs-binding.xml" /><br>    <import resource="classpath:META-INF/cxf/cxf-servlet.xml" /><br><br>    <bean id="jasperRestService" <br>          class="com.blogspot.diegopacheco.rest.report.JasperRestServiceImpl" /><br><br>    <jaxrs:server id="restJasperServer" address="/report/"><br>        <jaxrs:serviceBeans><br>            <ref bean="jasperRestService" /><br>        </jaxrs:serviceBeans><br>    </jaxrs:server><br>   <br></beans><br>

The import resource statement are needed for CXF works. We also have the jasperRestService bean definition that is really straight forward. The tag jaxrs:server is used for expose the spring service as REST.

This is it you can hit the url for instance: http://localhost/rest/report/pdf/reportTest/Diego

Cheers,

Popular posts from this blog

Kafka Streams with Java 15

Rust and Java Interoperability

HMAC in Java