Commits

Anonymous committed 99d7285

Issue number: WW-222
Submitted by: Rainer Hermanns
Reviewed by: Cameron Braid (cameron@datacodex.net)
Addition of JasperReports support.
Addition of example of using jaspoer reports.

git-svn-id: http://svn.opensymphony.com/svn/webwork/trunk@101573baa09-0c28-0410-bef9-dab3c582ae83

  • Participants
  • Parent commits 36293cc

Comments (0)

Files changed (25)

File lib/core/bsh-1.2b6.jar

Binary file added.

File lib/core/commons-beanutils-1.5.jar

Binary file added.

File lib/core/commons-collections-2.1.jar

Binary file added.

File lib/core/commons-digester-1.3.jar

Binary file added.

File lib/core/itext-0.96.jar

Binary file added.

File lib/core/jakarta-poi-1.5.1-final-20020615.jar

Binary file added.

File lib/core/jasperreports-0.5.0.jar

Binary file added.

File src/etc/example/xwork.xml

             <result name="input">validationForm.vm</result>
             <result name="error">validationForm.vm</result>
         </action>
+
+		 <!-- JasperReport Basic Test Actions -->
+        <action name="jasperTest" class="com.opensymphony.webwork.example.jasperreports.OrderListAction">
+            <param name="dataSource">orders</param>
+            <result name="success" type="dispatcher">
+                <param name="location">orderList.jasper</param>
+            </result>
+            <interceptor-ref name="defaultStack"/>
+        </action>
+
+        <action name="jasperPDFTest" class="com.opensymphony.webwork.example.jasperreports.OrderListAction">
+            <param name="dataSource">orders</param>
+            <param name="format">PDF</param>
+            <result name="success" type="dispatcher">
+                <param name="location">orderList.jasper</param>
+            </result>
+            <interceptor-ref name="defaultStack"/>
+        </action>
+
+        <action name="jasperCSVTest" class="com.opensymphony.webwork.example.jasperreports.OrderListAction">
+            <param name="dataSource">orders</param>
+            <param name="format">CSV</param>
+            <result name="success" type="dispatcher">
+                <param name="location">orderList.jasper</param>
+            </result>
+            <interceptor-ref name="defaultStack"/>
+        </action>
+        <action name="jasperHTMLTest" class="com.opensymphony.webwork.example.jasperreports.OrderListAction">
+            <param name="dataSource">orders</param>
+            <param name="format">HTML</param>
+            <result name="success" type="dispatcher">
+                <param name="location">orderList.jasper</param>
+            </result>
+            <interceptor-ref name="defaultStack"/>
+        </action>
+        <action name="jasperXLSTest" class="com.opensymphony.webwork.example.jasperreports.OrderListAction">
+            <param name="dataSource">orders</param>
+            <param name="format">XLS</param>
+            <result name="success" type="dispatcher">
+                <param name="location">orderList.jasper</param>
+            </result>
+            <interceptor-ref name="defaultStack"/>
+        </action>
+        <action name="jasperXMLTest" class="com.opensymphony.webwork.example.jasperreports.OrderListAction">
+            <param name="dataSource">orders</param>
+            <param name="format">XML</param>
+            <result name="success" type="dispatcher">
+                <param name="location">orderList.jasper</param>
+            </result>
+            <interceptor-ref name="defaultStack"/>
+        </action>
+
     </package>
 </xwork>

File src/example/com/opensymphony/webwork/example/jasperreports/OrderListAction.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.webwork.example.jasperreports;
+
+import com.opensymphony.webwork.views.jasperreports.JasperReportAction;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+
+/**
+ * A list of orders each with a list of line items
+ *
+ * Ported to WebWork2:
+ *
+ * @author Peter Kelley (peterk@moveit.com.au)
+ * @author &lt;a href="hermanns@aixcept.de"&gt;Rainer Hermanns&lt;/a&gt;
+ * @version $Id$
+ */
+public class OrderListAction extends JasperReportAction {
+    //~ Static fields/initializers /////////////////////////////////////////////
+
+    // Attributes ----------------------------------------------------
+    static List orders;
+
+    static {
+        // This never changes, so we do it once only
+        orders = new ArrayList();
+        class Order {
+            String customerName;
+            String address1;
+            String address2;
+            String city;
+            String state;
+            String postcode;
+            List lineItems;
+
+            public String getCustomerName() {
+                return customerName;
+            }
+
+            public void setCustomerName(String newCustomerName) {
+                customerName = newCustomerName;
+            }
+
+            public String getAddress1() {
+                return address1;
+            }
+
+            public void setAddress1(String newAddress1) {
+                address1 = newAddress1;
+            }
+
+            public String getAddress2() {
+                return address2;
+            }
+
+            public void setAddress2(String newAddress2) {
+                address2 = newAddress2;
+            }
+
+            public String getCity() {
+                return city;
+            }
+
+            public void setCity(String newCity) {
+                city = newCity;
+            }
+
+            public String getState() {
+                return state;
+            }
+
+            public void setState(String newState) {
+                state = newState;
+            }
+
+            public String getPostcode() {
+                return postcode;
+            }
+
+            public void setPostcode(String newPostcode) {
+                postcode = newPostcode;
+            }
+
+            public List getLineItems() {
+                return lineItems;
+            }
+
+            public void setLineItems(List newLineItems) {
+                lineItems = newLineItems;
+            }
+        }
+
+        class LineItem {
+            String productName;
+            Integer quantity;
+            Float unitCost;
+
+            public String getProductName() {
+                return productName;
+            }
+
+            public void setProductName(String newProductName) {
+                productName = newProductName;
+            }
+
+            public Integer getQuantity() {
+                return quantity;
+            }
+
+            public void setQuantity(Integer newQuantity) {
+                quantity = newQuantity;
+            }
+
+            public Float getUnitCost() {
+                return unitCost;
+            }
+
+            public void setUnitCost(Float newUnitCost) {
+                unitCost = newUnitCost;
+            }
+        }
+
+        // We read the values from a file so that it is easy to change
+        try {
+            InputStream resource = OrderListAction.class.getResourceAsStream("orders.txt");
+            BufferedReader in = new BufferedReader(new InputStreamReader(resource));
+            String thisLine;
+            List lineItems = null;
+            Order thisOrder = null;
+
+            while ((thisLine = in.readLine()) != null) {
+                StringTokenizer tokens = new StringTokenizer(thisLine, ",");
+
+                if ("order".equals(tokens.nextToken())) {
+                    if (thisOrder != null) {
+                        thisOrder.setLineItems(lineItems);
+                        orders.add(thisOrder);
+                    }
+
+                    thisOrder = new Order();
+                    lineItems = new ArrayList();
+                    thisOrder.setCustomerName(tokens.nextToken());
+                    thisOrder.setAddress1(tokens.nextToken());
+                    thisOrder.setAddress2(tokens.nextToken());
+                    thisOrder.setCity(tokens.nextToken());
+                    thisOrder.setState(tokens.nextToken());
+                    thisOrder.setPostcode(tokens.nextToken());
+                } else {
+                    //Line Item
+                    LineItem lineItem = new LineItem();
+                    lineItem.setProductName(tokens.nextToken());
+                    lineItem.setQuantity(new Integer(tokens.nextToken()));
+                    lineItem.setUnitCost(new Float(tokens.nextToken()));
+                    lineItems.add(lineItem);
+                }
+            }
+
+            thisOrder.setLineItems(lineItems);
+            orders.add(thisOrder);
+            in.close();
+        } catch (Throwable e) {
+            e.printStackTrace(System.err);
+            System.err.println("Could not read list of orders");
+        }
+    }
+
+	public OrderListAction()
+	{
+		setDataSource("orders");
+	}
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    public Object[] getOrderArray() {
+        return orders.toArray();
+    }
+
+    // Public --------------------------------------------------------
+    public List getOrders() {
+        return orders;
+    }
+
+    public String getTitle() {
+        return "Dynamic Order Report Title";
+    }
+    
+	public String getNullpx()
+	{
+		return "here";
+	}
+}

File src/example/com/opensymphony/webwork/example/jasperreports/orders.txt

+order,Mr Testy Tester,Level 12 Test Tower,1 Test Street,Test,NSW,2000
+line,Big Blue Widget,5,57.16
+line,Small Red Widget,11,23.14
+line,New Shiny Widget,1,109.12
+order,Mr Tested Tester, ,2 Test Street,Test Creek,NSW,2601
+line,Big Blue Widget,1,57.16
+line,Small Red Widget,53,23.14
+order,Mr Testier Tester, ,57/3 Test Street,Test Springs,NSW,2865
+line,New Shiny Widget,13,109.12

File src/java/com/opensymphony/webwork/util/MakeIterator.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.webwork.util;
+
+import java.lang.reflect.Array;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Map;
+
+
+/**
+ * MakeIterator
+ *
+ * Taken from WebWork 1.x by:
+ * @author &lt;a href="hermanns@aixcept.de"&gt;Rainer Hermanns&lt;/a&gt;
+ * @version $Id$
+ */
+public class MakeIterator {
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    /**
+     * Determine whether a given object can be made into an <code>Iterator</code>
+     * @param object the object to check
+     * @return <code>true</code> if the object can be converted to an iterator and
+     * <code>false</code> otherwise
+     */
+    public static boolean isIterable(Object object) {
+        if (object == null) {
+            return false;
+        }
+
+        if (object instanceof Map) {
+            return true;
+        } else if (object instanceof Collection) {
+            return true;
+        } else if (object.getClass().isArray()) {
+            return true;
+        } else if (object instanceof Enumeration) {
+            return true;
+        } else if (object instanceof Iterator) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public static Iterator convert(Object value) {
+        Iterator iterator;
+
+        if (value instanceof Map) {
+            value = ((Map) value).entrySet();
+        }
+
+        if (value instanceof Collection) {
+            iterator = ((Collection) value).iterator();
+        } else if (value.getClass().isArray()) {
+            //need ability to support primitives; therefore, cannot
+            //use Object[] casting.
+            Object a = Array.newInstance(value.getClass().getComponentType(), (Array.getLength(value)));
+            ArrayList list = new ArrayList(Array.getLength(value));
+
+            for (int j = 0; j < Array.getLength(value); j++) {
+                list.add(Array.get(value, j));
+            }
+
+            iterator = list.iterator();
+        } else if (value instanceof Enumeration) {
+            Enumeration enum = (Enumeration) value;
+            ArrayList list = new ArrayList();
+
+            while (enum.hasMoreElements()) {
+                list.add(enum.nextElement());
+            }
+
+            iterator = list.iterator();
+        } else {
+            iterator = (Iterator) value;
+        }
+
+        return iterator;
+    }
+}

File src/java/com/opensymphony/webwork/views/jasperreports/CompileReport.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.webwork.views.jasperreports;
+
+import dori.jasper.engine.JRException;
+import dori.jasper.engine.JasperCompileManager;
+
+
+/**
+ * Ported to WebWork2:
+ *
+ * @author &lt;a href="hermanns@aixcept.de"&gt;Rainer Hermanns&lt;/a&gt;
+ * @version $Id$
+ */
+public class CompileReport {
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    public static void main(String[] args) {
+        if (args.length < 1) {
+            System.out.println("Please supply the name of the report(s) source to compile.");
+            System.exit(-1);
+        }
+
+        try {
+            for (int i = 0; i < args.length; i++) {
+                System.out.println("JasperReports Compiling: " + args[i]);
+                JasperCompileManager.compileReportToFile(args[i]);
+            }
+        } catch (JRException e) {
+            e.printStackTrace();
+            System.exit(-1);
+        }
+
+        System.exit(0);
+    }
+}

File src/java/com/opensymphony/webwork/views/jasperreports/JasperReportAction.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.webwork.views.jasperreports;
+
+import com.opensymphony.webwork.ServletActionContext;
+
+import com.opensymphony.xwork.ActionSupport;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * <code>JasperReportAction</code>
+ *
+ * @author <a href="mailto:hermanns@aixcept.de">Rainer Hermanns</a>
+ * @version $Id$
+ */
+public class JasperReportAction extends ActionSupport implements JasperReportConstants {
+    //~ Static fields/initializers /////////////////////////////////////////////
+
+    /** jakarta-commons-logging reference */
+    private final static Log log = LogFactory.getLog(JasperReportAction.class);
+
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    /** data source static param */
+    private String dataSource;
+
+    /** format static param */
+    private String format;
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    /**
+     * Sets the dataSource for the requested report from the static params.
+     *
+     * @param dataSource - datasource attribute from static params
+     */
+    public void setDataSource(String dataSource) {
+        this.dataSource = dataSource;
+    }
+
+    /**
+     * Returns the dataSource for the requested report from the static params.
+     *
+     * @return dataSource - datasource attribute from static params
+     */
+    public String getDataSource() {
+        return dataSource;
+    }
+
+    /**
+     * Sets the output format of the report.
+     *
+     * @param format - the output format of the report.
+     */
+    public void setFormat(String format) {
+        this.format = format;
+    }
+
+    /**
+     * Returns the output format of the report.
+     *
+     * @return format - the output format of the report.
+     */
+    public String getFormat() {
+        return format;
+    }
+
+    /**
+     * Sets the static params format and dataSource from static params as request attributes and forwards to the
+     * JasperReportViewServlet.
+     *
+     * @return SUCCESS, if all parameters are given, ERROR otherwise
+     * @throws Exception
+     */
+    public String execute() throws Exception {
+        super.execute();
+
+        if (format == null) {
+            format = FORMAT_PDF;
+        }
+
+        if (dataSource == null) {
+            addActionError("No datasource was specified...");
+
+            if (log.isErrorEnabled()) {
+                log.error("JasperReportAction - [execute]: No dataSource specified...");
+            }
+
+            return ERROR;
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("JasperReportAction - [execute]: Creating JasperReport for dataSource=" + dataSource + ", format=" + format);
+        }
+
+        ServletActionContext.getRequest().setAttribute("dataSource", dataSource);
+        ServletActionContext.getRequest().setAttribute("format", format);
+
+        // Forward to the next page
+        return SUCCESS;
+    }
+}

File src/java/com/opensymphony/webwork/views/jasperreports/JasperReportConstants.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.webwork.views.jasperreports;
+
+
+/**
+ * <code>JasperReportConstants</code>
+ *
+ * @author <a href="mailto:hermanns@aixcept.de">Rainer Hermanns</a>
+ * @version $Id$
+ */
+public interface JasperReportConstants {
+    //~ Static fields/initializers /////////////////////////////////////////////
+
+    /** PDF format constant */
+    public static final String FORMAT_PDF = "PDF";
+
+    /** XML format constant */
+    public static final String FORMAT_XML = "XML";
+
+    /** HTML format constant */
+    public static final String FORMAT_HTML = "HTML";
+
+    /** XLS format constant */
+    public static final String FORMAT_XLS = "XLS";
+
+    /** CSV format constant */
+    public static final String FORMAT_CSV = "CSV";
+}

File src/java/com/opensymphony/webwork/views/jasperreports/JasperReportViewServlet.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.webwork.views.jasperreports;
+
+import com.opensymphony.webwork.ServletActionContext;
+
+import com.opensymphony.xwork.util.OgnlValueStack;
+
+import dori.jasper.engine.JRException;
+import dori.jasper.engine.JRExporter;
+import dori.jasper.engine.JRExporterParameter;
+import dori.jasper.engine.JasperExportManager;
+import dori.jasper.engine.JasperFillManager;
+import dori.jasper.engine.JasperManager;
+import dori.jasper.engine.JasperPrint;
+import dori.jasper.engine.JasperReport;
+import dori.jasper.engine.export.JRCsvExporter;
+import dori.jasper.engine.export.JRHtmlExporter;
+import dori.jasper.engine.export.JRHtmlExporterParameter;
+import dori.jasper.engine.export.JRXlsExporter;
+import dori.jasper.engine.export.JRXmlExporter;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+
+import java.util.Map;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+
+/**
+ * Provide a view of a webwork action as a jasper report. The report format will be determined by
+ * the format parameter passed in the xwork action definition.
+ * Example action definition:
+ *
+ * &lt;action name="jasperTest" class="com.opensymphony.webwork.views.jasperreports.example.OrderListAction"&gt;
+ *    &lt;param name="dataSource"&gt;orders&lt;/param&gt;
+ *    &lt;param name="format"&gt;PDF&lt;/param&gt;
+ *    &lt;result name="success" type="dispatcher"&gt;
+ *        &lt;param name="location"&gt;orderList.jasper&lt;/param&gt;
+ *    &lt;/result&gt;
+ *    &lt;interceptor-ref name="defaultStack"/&gt;
+ * &lt;/action&gt;
+ *
+ * Valid report format strings are the following:
+ * &lt;ul&gt;
+ * &lt;li&gt;PDF&lt;/li&gt;
+ * &lt;li&gt;XML&lt;/li&gt;
+ * &lt;li&gt;HTML&lt;/li&gt;
+ * &lt;li&gt;XLS&lt;/li&gt;
+ * &lt;li&gt;CSV&lt;/li&gt;
+ * &lt;/ul&gt;
+ * It will then look for a compiled report definition of the form &lt;code&gt;&lt;view name&gt;.jasper&lt;/code&gt;,
+ * run it to the appropriate output format and stream the results as the HTTP response.
+ *
+ * Ported to WebWork2:
+ *
+ * @author &lt;a href="hermanns@aixcept.de"&gt;Rainer Hermanns&lt;/a&gt;
+ * @version $Id$
+ */
+public class JasperReportViewServlet extends HttpServlet implements JasperReportConstants {
+    //~ Static fields/initializers /////////////////////////////////////////////
+
+    protected static Log log = LogFactory.getLog(JasperReportViewServlet.class);
+
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    protected String IMAGES_URI = "/images/";
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    /* (non-Javadoc)
+     * @see javax.servlet.GenericServlet#init()
+     */
+    public void init() throws ServletException {
+        super.init();
+
+        if (getServletConfig().getInitParameter("IMAGES_URI") != null) {
+            IMAGES_URI = getServletConfig().getInitParameter("IMAGES_URI");
+        }
+    }
+
+    /**
+     * Service a HTTP request
+     * @param request the request to service
+     * @param response the response to send to the client
+     */
+    public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException {
+        //construct the data source for the report
+        OgnlValueStack stack = ServletActionContext.getContext().getValueStack();
+        String dataSource = "" + request.getAttribute("dataSource");
+        OgnlValueStackDataSource stackDataSource = new OgnlValueStackDataSource(stack, dataSource);
+
+        //get the output format
+        String outputFormat = "" + request.getAttribute("format");
+
+        if (outputFormat == null) {
+            outputFormat = FORMAT_PDF;
+        }
+
+        if (!"contype".equals(request.getHeader("User-Agent"))) {
+            // Determine the directory that the report file is in and set the reportDirectory parameter
+            String systemId = getServletContext().getRealPath(request.getServletPath());
+            Map parameters = new OgnlValueStackShadowMap(stack);
+            File directory = new File(systemId.substring(0, systemId.lastIndexOf(File.separator)));
+            parameters.put("reportDirectory", directory);
+
+            byte[] output = null;
+            JasperPrint jasperPrint = null;
+
+            // Fill the report and produce a print object
+            try {
+                JasperReport jasperReport = JasperManager.loadReport(systemId);
+                jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, stackDataSource);
+            } catch (JRException e) {
+                log.error("Error building report for uri " + systemId, e);
+                throw new ServletException(e.getMessage(), e);
+            }
+
+            // Export the print object to the desired output format
+            try {
+                if (outputFormat.equals(FORMAT_PDF)) {
+                    response.setContentType("application/pdf");
+
+                    // response.setHeader("Content-disposition", "inline; filename=report.pdf");
+                    output = JasperExportManager.exportReportToPdf(jasperPrint);
+                } else {
+                    JRExporter exporter = null;
+
+                    if (outputFormat.equals(FORMAT_CSV)) {
+                        response.setContentType("text/plain");
+                        exporter = new JRCsvExporter();
+                    } else if (outputFormat.equals(FORMAT_HTML)) {
+                        // todo fixme... currently not working...
+                        response.setContentType("text/html");
+                        exporter = new JRHtmlExporter();
+                        ((JRHtmlExporter) exporter).setParameter(JRHtmlExporterParameter.IMAGES_URI, IMAGES_URI);
+                    } else if (outputFormat.equals(FORMAT_XLS)) {
+                        response.setContentType("application/vnd.ms-excel");
+                        exporter = new JRXlsExporter();
+                    } else if (outputFormat.equals(FORMAT_XML)) {
+                        response.setContentType("text/xml");
+                        exporter = new JRXmlExporter();
+                    } else {
+                        throw new ServletException("Unknown report format: " + outputFormat);
+                    }
+
+                    output = exportReportToBytes(jasperPrint, exporter);
+                }
+            } catch (JRException e) {
+                String message = "Error producing " + outputFormat + " report for uri " + systemId;
+                log.error(message, e);
+                throw new ServletException(e.getMessage(), e);
+            }
+
+            response.setContentLength(output.length);
+
+            ServletOutputStream ouputStream;
+
+            try {
+                if (log.isDebugEnabled()) {
+                    log.debug("Writing " + output.length + " bytes to output stream");
+                }
+
+                ouputStream = response.getOutputStream();
+                ouputStream.write(output);
+                ouputStream.flush();
+                ouputStream.close();
+            } catch (IOException e) {
+                log.error("Error writing report output", e);
+                throw new ServletException(e.getMessage(), e);
+            }
+        } else {
+            // Code to handle "contype" request from IE
+            try {
+                ServletOutputStream outputStream;
+                response.setContentType("application/pdf");
+                response.setContentLength(0);
+                outputStream = response.getOutputStream();
+                outputStream.close();
+            } catch (IOException e) {
+                log.error("Error writing report output", e);
+                throw new ServletException(e.getMessage(), e);
+            }
+        }
+    }
+
+    /**
+     * Run a Jasper report to CSV format and put the results in a byte array
+     * @param jasperPrint The Print object to render as CSV
+     * @param exporter The exporter to use to export the report
+     * @return A CSV formatted report
+     * @throws JRException If there is a problem running the report
+     */
+    private byte[] exportReportToBytes(JasperPrint jasperPrint, JRExporter exporter) throws JRException {
+        byte[] output;
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+        exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
+        exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, baos);
+
+        exporter.exportReport();
+
+        output = baos.toByteArray();
+
+        return output;
+    }
+}

File src/java/com/opensymphony/webwork/views/jasperreports/OgnlValueStackDataSource.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.webwork.views.jasperreports;
+
+import com.opensymphony.webwork.util.MakeIterator;
+
+import com.opensymphony.xwork.util.OgnlValueStack;
+
+import dori.jasper.engine.JRDataSource;
+import dori.jasper.engine.JRException;
+import dori.jasper.engine.JRField;
+
+import org.apache.commons.logging.*;
+
+import java.util.Iterator;
+
+
+/**
+ * Ported to WebWork2:
+ *
+ * @author &lt;a href="hermanns@aixcept.de"&gt;Rainer Hermanns&lt;/a&gt;
+ * @version $Id$
+ */
+public class OgnlValueStackDataSource implements JRDataSource {
+    //~ Static fields/initializers /////////////////////////////////////////////
+
+    /** Logger for this class */
+    private static Log log = LogFactory.getLog(OgnlValueStackDataSource.class);
+
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    Iterator iterator;
+    OgnlValueStack valueStack;
+    boolean firstTimeThrough = true;
+
+    //~ Constructors ///////////////////////////////////////////////////////////
+
+    /**
+     * Create a value stack data source on the given iterable property
+     *
+     * @param valueStack The value stack to base the data source on
+     * @param dataSource The property to iterate over for the report
+     */
+    public OgnlValueStackDataSource(OgnlValueStack valueStack, String dataSource) {
+        this.valueStack = valueStack;
+
+        Object dataSourceValue = valueStack.findValue(dataSource);
+
+        if (dataSourceValue != null) {
+            if (MakeIterator.isIterable(dataSourceValue)) {
+                iterator = MakeIterator.convert(dataSourceValue);
+            } else {
+                Object[] array = new Object[1];
+                array[0] = dataSourceValue;
+                iterator = MakeIterator.convert(array);
+            }
+        } else {
+            log.warn("Data source value for data source " + dataSource + " was null");
+        }
+    }
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    /**
+     * Get the value of a given field
+     * @param field The field to get the value for. The expression language to get the value
+     * of the field is either taken from the description property or from the name of the field
+     * if the description is <code>null</code>.
+     * @return an <code>Object</code> containing the field value or a new
+     * <code>OgnlValueStackDataSource</code> object if the field value evaluates to
+     * an object that can be iterated over.
+     * @throws JRException if there is a problem obtaining the value
+     */
+    public Object getFieldValue(JRField field) throws JRException {
+        //TODO: move the code to return a OgnlValueStackDataSource to a seperate
+        //      method when and if the JRDataSource interface is updated to support
+        //      this.
+        String expression = field.getDescription();
+
+        if (expression == null) {
+            //Description is optional so use the field name as a default
+            expression = field.getName();
+        }
+
+        Object value = valueStack.findValue(expression);
+
+        if (log.isDebugEnabled()) {
+            log.debug("field: " + field.getName() + "/" + value);
+        }
+
+        if (MakeIterator.isIterable(value)) {
+            //                return new OgnlValueStackDataSource(this.valueStack, field.getName());
+            return new OgnlValueStackDataSource(this.valueStack, expression);
+        } else {
+            return value;
+        }
+    }
+
+    /**
+     * Is there any more data
+     * @return <code>true</code> if there are more elements to iterate over and
+     * <code>false</code> otherwise
+     * @throws JRException if there is a problem determining whether there
+     * is more data
+     */
+    public boolean next() throws JRException {
+        if (firstTimeThrough) {
+            firstTimeThrough = false;
+        } else {
+            valueStack.pop();
+        }
+
+        if ((iterator != null) && (iterator.hasNext())) {
+            valueStack.push(iterator.next());
+            log.debug("Pushed next value: " + valueStack.findValue("."));
+
+            return true;
+        } else {
+            log.debug("No more values");
+
+            return false;
+        }
+    }
+}

File src/java/com/opensymphony/webwork/views/jasperreports/OgnlValueStackShadowMap.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.webwork.views.jasperreports;
+
+import com.opensymphony.xwork.util.OgnlValueStack;
+
+import java.util.HashMap;
+import java.util.Set;
+
+
+/**
+ * Ported to WebWork2:
+ *
+ * @author &lt;a href="hermanns@aixcept.de"&gt;Rainer Hermanns&lt;/a&gt;
+ * @version $Id$
+ */
+public class OgnlValueStackShadowMap extends HashMap {
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    /** valueStack reference */
+    OgnlValueStack valueStack;
+
+    /** entries reference */
+    Set entries;
+
+    //~ Constructors ///////////////////////////////////////////////////////////
+
+    /**
+     * Constructs an instance of OgnlValueStackShadowMap.
+     *
+     * @param valueStack - the underlying valuestack
+     */
+    public OgnlValueStackShadowMap(OgnlValueStack valueStack) {
+        this.valueStack = valueStack;
+    }
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    /**
+     * Implementation of containsKey(), overriding HashMap implementation.
+     *
+     * @param key - The key to check in HashMap and if not found to check on valueStack.
+     * @return <tt>true</tt>, if conatins key, <tt>false</tt> otherwise.
+     * @see java.util.HashMap#containsKey
+     */
+    public boolean containsKey(Object key) {
+        boolean hasKey = super.containsKey(key);
+
+        if (!hasKey) {
+            if (valueStack.findValue((String) key) != null) {
+                hasKey = true;
+            }
+        }
+
+        return hasKey;
+    }
+
+    /**
+     * Implementation of get(), overriding HashMap implementation.
+     *
+     * @param key - The key to get in HashMap and if not found there from the valueStack.
+     * @return value - The object from HashMap or if null, from the valueStack.
+     * @see java.util.HashMap#get
+     */
+    public Object get(Object key) {
+        Object value = super.get(key);
+
+        if ((value == null) && key instanceof String) {
+            value = valueStack.findValue((String) key);
+        }
+
+        return value;
+    }
+}

File src/webapp/WEB-INF/web.xml

         <load-on-startup>1</load-on-startup>
     </servlet>
 
+    <servlet>
+        <servlet-name>jasperreports</servlet-name>
+        <servlet-class>com.opensymphony.webwork.views.jasperreports.JasperReportViewServlet</servlet-class>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+   <servlet-mapping>
+        <servlet-name>jasperreports</servlet-name>
+        <url-pattern>*.jasper</url-pattern>
+    </servlet-mapping>
+
     <servlet-mapping>
         <servlet-name>velocity</servlet-name>
         <url-pattern>*.vm</url-pattern>

File src/webapp/default.jsp

 Click <a href="expressionValidation.action">here</a> for a visitor validation example with expression validation<br>
 Click <a href="redirect.action">here</a> to be test the ServletRedirectResult using a location resolved via ognl<br>
 Click <a href="parseLocation.action">here</a> to view the FormTag example using a location resolved via ognl<br>
+Click <a href="jasperTest.action">here</a> for a jasper reports PDF example<br>
+Click <a href="jasperHTMLTest.action">here</a> for a jasper reports HTML example<br>
+Click <a href="jasperXMLTest.action">here</a> for a jasper reports XML example<br>
+Click <a href="jasperCSVTest.action">here</a> for a jasper reports CSV example<br>

File src/webapp/images/px

Binary file added.

File src/webapp/images/px.readme.txt

+the file "/images/px" is a gif image of a single pixek to use as a shim/spacer image for JasperReports.

File src/webapp/orderList.jasper

Binary file added.

File src/webapp/orderList.xml

+<?xml version="1.0"?>
+<!DOCTYPE jasperReport PUBLIC "-//JasperReports//DTD Report Design//EN" "http://jasperreports.sourceforge.net/dtds/jasperreport.dtd">
+
+<jasperReport
+    name="orderList"
+    pageWidth="595"
+    pageHeight="842"
+    columnWidth="515"
+    columnSpacing="0"
+    leftMargin="40"
+    rightMargin="40"
+    topMargin="50"
+    bottomMargin="50">
+    <reportFont name="Arial_Normal" isDefault="true" fontName="Arial" size="12" pdfFontName="Helvetica" pdfEncoding="Cp1252" isPdfEmbedded="false"/>
+    <reportFont name="Arial_Small" isDefault="true" fontName="Arial" size="8" pdfFontName="Helvetica" pdfEncoding="Cp1252" isPdfEmbedded="false"/>
+    <reportFont name="Arial_Bold" isDefault="false" fontName="Arial" size="12" isBold="true" pdfFontName="Helvetica-Bold" pdfEncoding="Cp1252" isPdfEmbedded="false"/>
+    <reportFont name="Arial_Italic" isDefault="false" fontName="Arial" size="12" isItalic="true" pdfFontName="Helvetica-Oblique" pdfEncoding="Cp1252" isPdfEmbedded="false"/>
+    <parameter name="title" class="java.lang.String"/>
+    <parameter name="reportDirectory" class="java.io.File"/>
+    <field name="customerName" class="java.lang.String">
+        <fieldDescription>customerName</fieldDescription>
+    </field>
+    <field name="addressLine1" class="java.lang.String">
+        <fieldDescription>address1</fieldDescription>
+    </field>
+    <field name="addressLine2" class="java.lang.String">
+        <fieldDescription>address2</fieldDescription>
+    </field>
+    <field name="city" class="java.lang.String">
+        <fieldDescription>city</fieldDescription>
+    </field>
+    <field name="state" class="java.lang.String">
+        <fieldDescription>state</fieldDescription>
+    </field>
+    <field name="postcode" class="java.lang.String">
+        <fieldDescription>postcode</fieldDescription>
+    </field>
+    <field name="lineItems" class="java.lang.Object">
+        <fieldDescription>lineItems</fieldDescription>
+    </field>
+    <title>
+        <band height="50">
+            <line>
+                <reportElement x="0" y="0" width="515" height="0"/>
+                <graphicElement stretchType="NoStretch"/>
+            </line>
+            <textField isStretchWithOverflow="true">
+                <reportElement x="0" y="10" width="515" height="30"/>
+                <textElement textAlignment="Center" lineSpacing="Single">
+                    <font reportFont="Arial_Normal" size="22"/>
+                </textElement>
+                <textFieldExpression>$P{title}</textFieldExpression>
+            </textField>
+        </band>
+    </title>
+    <pageHeader>
+        <band height="20">
+            <rectangle>
+                <reportElement x="0" y="5" width="515" height="15" backcolor="#666666"/>
+                <graphicElement stretchType="NoStretch" pen="None"/>
+            </rectangle>
+            <staticText>
+                <reportElement x="5" y="5" width="100" height="15" forecolor="#FFFFFF"/>
+                <textElement>
+                    <font reportFont="Arial_Bold"/>
+                </textElement>
+                <text>Order List</text>
+            </staticText>
+            <line>
+                <reportElement x="0" y="20" width="515" height="0"/>
+                <graphicElement stretchType="NoStretch"/>
+            </line>
+        </band>
+    </pageHeader>
+    <columnHeader>
+        <band height="0"/>
+    </columnHeader>
+    <detail>
+        <band height="100">
+            <textField isStretchWithOverflow="true">
+                <reportElement x="5" y="5" width="100" height="15"/>
+                <textElement>
+                    <font reportFont="Arial_Bold"/>
+                </textElement>
+                <textFieldExpression>$F{customerName}</textFieldExpression>
+            </textField>
+            <line>
+                <reportElement x="0" y="20" width="515" height="0"/>
+                <graphicElement stretchType="NoStretch"/>
+            </line>
+            <subreport isUsingCache="true">
+                <reportElement x="5" y="25" width="200" height="20" backcolor="#DDFFFF"/>
+                <subreportParameter name="customerName">
+                    <subreportParameterExpression>
+                        $F{customerName}
+                    </subreportParameterExpression>
+                </subreportParameter>
+                <dataSourceExpression>
+                    $F{lineItems}
+                </dataSourceExpression>
+                <subreportExpression class="java.lang.String">
+                    $P{reportDirectory}.getCanonicalPath() + java.io.File.separator + "orderReport.jasper"
+                </subreportExpression>
+            </subreport>
+            <textField>
+                <reportElement x="210" y="25" width="300" height="15"/>
+                <textElement>
+                    <font reportFont="Arial_Small"/>
+                </textElement>
+                <textFieldExpression>$F{customerName}</textFieldExpression>
+            </textField>
+            <textField>
+                <reportElement x="210" y="40" width="300" height="15"/>
+                <textElement>
+                    <font reportFont="Arial_Small"/>
+                </textElement>
+                <textFieldExpression>$F{addressLine1}</textFieldExpression>
+            </textField>
+            <textField>
+                <reportElement x="210" y="55" width="300" height="15"/>
+                <textElement>
+                    <font reportFont="Arial_Small"/>
+                </textElement>
+                <textFieldExpression>$F{addressLine2}</textFieldExpression>
+            </textField>
+            <textField>
+                <reportElement x="210" y="70" width="300" height="15"/>
+                <textElement>
+                    <font reportFont="Arial_Small"/>
+                </textElement>
+                <textFieldExpression>$F{city}</textFieldExpression>
+            </textField>
+            <textField>
+                <reportElement x="210" y="85" width="300" height="15"/>
+                <textElement>
+                    <font reportFont="Arial_Small"/>
+                </textElement>
+                <textFieldExpression>$F{state} + " " + $F{postcode}</textFieldExpression>
+            </textField>
+        </band>
+    </detail>
+    <columnFooter>
+        <band height="0"/>
+    </columnFooter>
+    <pageFooter>
+        <band height="40">
+            <line>
+                <reportElement x="0" y="10" width="515" height="0"/>
+                <graphicElement stretchType="NoStretch"/>
+            </line>
+            <textField>
+                <reportElement x="200" y="20" width="80" height="15"/>
+                <textElement textAlignment="Right"/>
+                <textFieldExpression class="java.lang.String">
+                    "Page " + String.valueOf($V{PAGE_NUMBER}) + " of"
+                </textFieldExpression>
+            </textField>
+            <textField evaluationTime="Report">
+                <reportElement x="280" y="20" width="75" height="15"/>
+                <textElement textAlignment="Left"/>
+                <textFieldExpression class="java.lang.String">
+                    " " + String.valueOf($V{PAGE_NUMBER})
+                </textFieldExpression>
+            </textField>
+        </band>
+    </pageFooter>
+    <summary>
+        <band height="0"/>
+    </summary>
+</jasperReport>

File src/webapp/orderReport.jasper

Binary file added.

File src/webapp/orderReport.xml

+<?xml version="1.0"?>
+<!DOCTYPE jasperReport PUBLIC "-//JasperReports//DTD Report Design//EN" "http://jasperreports.sourceforge.net/dtds/jasperreport.dtd">
+
+<jasperReport 
+		name="orderReport" 
+		pageWidth="200" 
+		pageHeight="842" 
+		columnWidth="200" 
+		columnSpacing="0" 
+		leftMargin="0" 
+		rightMargin="0" 
+		topMargin="0" 
+		bottomMargin="0">
+	<reportFont name="Arial_Normal" isDefault="true" fontName="Arial" size="8" pdfFontName="Helvetica" pdfEncoding="Cp1252" isPdfEmbedded="false"/>
+	<reportFont name="Arial_Bold" isDefault="false" fontName="Arial" size="8" isBold="true" pdfFontName="Helvetica-Bold" pdfEncoding="Cp1252" isPdfEmbedded="false"/>
+	<reportFont name="Arial_Italic" isDefault="false" fontName="Arial" size="8" isItalic="true" pdfFontName="Helvetica-Oblique" pdfEncoding="Cp1252" isPdfEmbedded="false"/>
+	<parameter name="customerName" class="java.lang.String"/>
+	<field name="productName" class="java.lang.String"/>
+	<field name="quantity" class="java.lang.Integer"/>
+	<field name="unitCost" class="java.lang.Float"/>
+	<variable name="itemTotal" class="java.math.BigDecimal" resetType="None" calculation="Nothing">
+		<variableExpression>
+			(new BigDecimal($F{quantity}.intValue() * $F{unitCost}.doubleValue())).setScale(2,java.math.BigDecimal.ROUND_HALF_UP)
+		</variableExpression>
+	</variable>
+	<variable name="orderTotal" class="java.math.BigDecimal" resetType="Report" calculation="Sum">
+		<variableExpression>
+			$V{itemTotal}
+		</variableExpression>
+	</variable>
+	<title>
+		<band height="19">
+			<textField>
+				<reportElement x="0" y="2" width="200" height="15"/>
+				<textElement textAlignment="Center">
+					<font reportFont="Arial_Bold"/>
+				</textElement>
+				<textFieldExpression>"Line Items for " + $P{customerName}</textFieldExpression>
+			</textField>
+		</band>
+	</title>
+	<pageHeader>
+		<band height="19">
+			<rectangle>
+				<reportElement x="0" y="2" width="200" height="15" forecolor="#DDDDDD" backcolor="#DDDDDD"/>
+				<graphicElement stretchType="NoStretch"/>
+			</rectangle>
+			<staticText>
+				<reportElement x="5" y="2" width="100" height="15"/>
+				<textElement textAlignment="Left">
+					<font reportFont="Arial_Bold"/>
+				</textElement>
+				<text>Product</text>
+			</staticText>
+			<staticText>
+				<reportElement x="110" y="2" width="40" height="15"/>
+				<textElement textAlignment="Right">
+					<font reportFont="Arial_Bold"/>
+				</textElement>
+				<text>Quantity</text>
+			</staticText>
+			<staticText>
+				<reportElement x="155" y="2" width="40" height="15"/>
+				<textElement textAlignment="Right">
+					<font reportFont="Arial_Bold"/>
+				</textElement>
+				<text>Cost</text>
+			</staticText>
+		</band>		
+	</pageHeader>
+	<columnHeader>
+		<band height="0"/>
+	</columnHeader>
+	<detail>
+		<band height="19">
+			<textField>
+				<reportElement x="5" y="2" width="100" height="15"/>
+				<textElement textAlignment="Left"/>
+				<textFieldExpression class="java.lang.String">
+					$F{productName}
+				</textFieldExpression>
+			</textField>
+			<textField isStretchWithOverflow="true">
+				<reportElement x="110" y="2" width="40" height="15" positionType="Float"/>
+				<textElement textAlignment="Right"/>
+				<textFieldExpression class="java.lang.Integer">
+					$F{quantity}
+				</textFieldExpression>
+			</textField>
+			<textField isStretchWithOverflow="true">
+				<reportElement x="155" y="2" width="40" height="15" positionType="Float"/>
+				<textElement textAlignment="Right"/>
+				<textFieldExpression class="java.math.BigDecimal">
+					$V{itemTotal}
+				</textFieldExpression>
+			</textField>
+		</band>
+	</detail>
+	<columnFooter>
+		<band height="0"/>
+	</columnFooter>
+	<pageFooter>
+		<band height="0"/>
+	</pageFooter>
+	<summary>
+		<band height="19">
+			<line>
+				<reportElement x="0" y="2" width="200" height="0"/>
+				<graphicElement stretchType="NoStretch" pen="Thin"/>
+			</line>
+			<staticText>
+				<reportElement x="100" y="2" width="50" height="15"/>
+				<textElement textAlignment="Right" lineSpacing="Single">
+					<font reportFont="Arial_Bold"/>
+				</textElement>
+				<text>Total : </text>
+			</staticText>
+			<textField isStretchWithOverflow="true">
+				<reportElement x="155" y="2" width="40" height="15"/>
+				<textElement textAlignment="Right" lineSpacing="Single">
+					<font reportFont="Arial_Bold"/>
+				</textElement>
+				<textFieldExpression class="java.math.BigDecimal">
+					$V{orderTotal}.setScale(2,java.math.BigDecimal.ROUND_HALF_UP)
+				</textFieldExpression>
+			</textField>
+		</band>
+	</summary>
+</jasperReport>