Monday, November 12, 2012

Locking Your Solution’s VBA Project

Office 2000
8 out of 14 rated this helpful - Rate this topic
You can prevent users from viewing code by locking each VBA project associated with your solution. To lock a project, you define a password that must be entered before a user can view the project in the Visual Basic Editor. You can lock the VBA project in Word, Excel, Access, PowerPoint, Outlook, and FrontPage. This section covers the procedure for locking a VBA project for all of these applications. For more information about security in Access, see Chapter 18, "Securing Access Databases."
You can lock the VBA project contained within a document, template, or database. For Outlook, you can lock the VBA project for the local session of Outlook, which is stored in the VbaProject.OTM file. Similarly, for FrontPage, you can lock the VBA project for the local session of FrontPage, which is stored in the Microsoft FrontPage.fpm file. You can lock only an entire VBA project, not individual components within the project. This means that when you lock a VBA project, you are controlling access to all standard modules (including the default ThisDocument, ThisWorkbook, and ThisOutlookSession modules for Word, Excel, and Outlook, respectively), all class modules, and all UserForms contained by the project.
Important   VBA project passwords are case-sensitive. To view a locked VBA project, you must use the exact case you used when setting the password. If you lose or forget the password, there is no way to view the locked VBA project. When password-protecting a VBA project, be sure to write down the password and keep it in a physically secured location.
To lock a VBA project for viewing
  1. Open the document, template, or database that contains the VBA project you want to protect. For Outlook or FrontPage, start Outlook or FrontPage on the computer that contains the VBA project you want to protect.
  2. Open the Visual Basic Editor.
  3. In the Project Explorer, right-click the project you want to protect, and then click ProjectName Properties on the shortcut menu.
  4. On the Protection tab, select the Lock project for viewing check box, enter and confirm the password, and then click OK.
The next time you open the document, the password you set will be required to view code in the project.
Important   You can set a password without selecting the Lock project for viewing check box, but doing so only controls access to the Project Properties dialog box itself. If you set a password without selecting the Lock project for viewing check box, users will be able to view and modify your code.
There is no way to programmatically specify a password for a locked VBA project. If you write code that attempts to work with components in a locked VBA project, the Visual Basic Editor will display a dialog box to prompt the user for the correct password. If the user specifies the correct password, your code will continue to run. If the user doesn't specify the correct password, a trappable error will be returned.
To determine if a VBA project is locked, set a reference to the Microsoft Visual Basic for Applications Extensibility 5.3 object library, and then inspect the Protection property of the VBA project. The following example shows how to check the Protection property of the VBA project in a Word document.
Function IsWordProjectLocked(strDocPath As String) As Boolean
   Dim vbpProj As VBIDE.VBProject
   Dim docProj As Word.Document

   ' Open document.
   Set docProj = Documents.Open(strDocPath)

   ' Set reference to document's VBA project.
   Set vbpProj = docProj.VBProject

   ' Check Protection property of VBA project.
   If vbpProj.Protection = vbext_pp_locked Then
      IsWordProjectLocked = True
   Else
      IsWordProjectLocked = False
   End If
End Sub
The IsWordProjectLocked procedure is available in the IsProjectLocked module in CheckProject.dot in the ODETools\V9\Samples\OPG\Samples\CH17 subfolder on the Office 2000 Developer CD-ROM.

Wednesday, November 7, 2012



This patch replaces Jasper included to version 3 and also contains a new
ReportStarter class that accepts complex Jasper reports packed as a zip file.



Index: .project
===================================================================
--- .project    (revision 5753)
+++ .project    (working copy)
@@ -6,16 +6,6 @@
     </projects>
     <buildSpec>
         <buildCommand>
-            <name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
-            <triggers>full,incremental,</triggers>
-            <arguments>
-                <dictionary>
-                    <key>LaunchConfigHandle</key>
-                    <value>&lt;project&gt;/.externalToolBuilders
                    /org.eclipse.wst.common.project.facet.core.builder.launch</value>
-                </dictionary>
-            </arguments>
-        </buildCommand>
-        <buildCommand>
             <name>org.eclipse.jdt.core.javabuilder</name>
             <arguments>
             </arguments>
Index: tools/lib/jfreechart-1.0.2.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: tools/lib/jfreechart-1.0.3.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: tools/lib/jfreechart-1.0.3.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: tools/build.xml
===================================================================
--- tools/build.xml    (revision 5753)
+++ tools/build.xml    (working copy)
@@ -360,7 +360,7 @@
         <patternset refid="manifest.exclude"/>
       </zipfileset>
       <!-- JFreeChart            -->
-      <zipfileset src="lib/jfreechart-1.0.2.jar" >
+      <zipfileset src="lib/jfreechart-1.0.3.jar" >
         <patternset refid="manifest.exclude"/>
       </zipfileset>
       <zipfileset src="lib/jcommon-1.0.5.jar" >
Index: .classpath
===================================================================
--- .classpath    (revision 5753)
+++ .classpath    (working copy)
@@ -46,7 +46,7 @@
     <classpathentry exported="true" kind="lib" path="tools/lib/jgraph.jar"/>
     <classpathentry exported="true" kind="lib" path="tools/lib/barbecue-1.1.jar"/>
     <classpathentry exported="true" kind="lib" path="tools/lib/jcommon-1.0.5.jar"/>
-    <classpathentry exported="true" kind="lib" path="tools/lib/jfreechart-1.0.2.jar"/>
+    <classpathentry exported="true" kind="lib" path="tools/lib/jfreechart-1.0.3.jar"/>
     <classpathentry exported="true" kind="lib" path="tools/lib/postgresql.jar"/>
     <classpathentry exported="true" kind="lib" path="posterita/src/web/WEB-INF/lib/struts.jar"/>
     <classpathentry exported="true" kind="lib" path="posterita/src/web/WEB-INF/lib/barcode4j.jar"/>
@@ -70,7 +70,7 @@
     <classpathentry exported="true" kind="lib" path="tools/lib/xml-apis.jar"/>
     <classpathentry exported="true" kind="lib" path="tools/lib/itext-1.4.8.jar"/>
     <classpathentry exported="true" kind="lib" path="tools/lib/jpedal.jar"/>
-    <classpathentry exported="true" kind="lib" path="JasperReportsTools/lib/jasperreports-1.3.0.jar"/>
+    <classpathentry exported="true" kind="lib" path="JasperReportsTools/lib/jasperreports-3.0.0.jar"/>
     <classpathentry exported="true" kind="lib" path="tools/lib/swingx-0.9.0.jar"/>
     <classpathentry exported="true" kind="lib" path="tools/lib/bsh-2.0b5.jar"/>
     <classpathentry exported="true" kind="lib" path="tools/lib/bsh-engine.jar"/>
Index: JasperReportsTools/lib/iReport.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: JasperReportsTools/lib/jfreechart-1.0.3.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: JasperReportsTools/lib/jfreechart-1.0.3.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: JasperReportsTools/lib/jasperreports-3.0.0.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: JasperReportsTools/lib/jasperreports-3.0.0.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: JasperReports/src/org/compiere/report/FileVisitor.java
===================================================================
--- JasperReports/src/org/compiere/report/FileVisitor.java    (revision 0)
+++ JasperReports/src/org/compiere/report/FileVisitor.java    (revision 0)
@@ -0,0 +1,34 @@
+package org.compiere.report;
+
+
+import java.io.File;
+
+public class FileVisitor {
+    public interface Visitor
+    {
+        void visitFile(java.io.File file) throws Exception;
+    }
+    public static void visitRecursive(java.io.File dir, Visitor visitor) throws Exception
+    {
+        File[] fs=dir.listFiles();
+        if(fs!=null)
+        {
+            for(File f: fs)
+            {
+                visitor.visitFile(f);
+                visitRecursive(f, visitor);
+            }
+        }
+    }
+    public static void visit(java.io.File dir, Visitor visitor) throws Exception
+    {
+        File[] fs=dir.listFiles();
+        if(fs!=null)
+        {
+            for(File f: fs)
+            {
+                visitor.visitFile(f);
+            }
+        }
+    }
+}
Index: JasperReports/src/org/compiere/report/ReportStarter2.java
===================================================================
--- JasperReports/src/org/compiere/report/ReportStarter2.java    (revision 0)
+++ JasperReports/src/org/compiere/report/ReportStarter2.java    (revision 0)
@@ -0,0 +1,439 @@
+/*
+ */
+package org.compiere.report;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Timestamp;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.logging.Level;
+
+import net.sf.jasperreports.engine.JasperExportManager;
+import net.sf.jasperreports.engine.JasperFillManager;
+import net.sf.jasperreports.engine.JasperPrint;
+import net.sf.jasperreports.engine.JasperPrintManager;
+import net.sf.jasperreports.engine.JasperReport;
+
+import org.compiere.db.ServerConnection;
+import org.compiere.process.ClientProcess;
+import org.compiere.process.ProcessCall;
+import org.compiere.process.ProcessInfo;
+import org.compiere.process.ProcessInfoParameter;
+import org.compiere.util.CLogger;
+import org.compiere.util.DB;
+import org.compiere.util.Env;
+import org.compiere.util.Ini;
+import org.compiere.util.Language;
+import org.compiere.util.Trx;
+import org.compiere.utils.DBUtils;
+
+/**
+ * @author rlemeill originaly coming from an application note from
+ *         compiere.co.uk --- Modifications: Allow Jasper Reports to be able to
+ *         be run on VPN profile (i.e: no direct connection to DB). Implemented
+ *         ClientProcess for it to run on client.
+ * @author Ashley Ramdass
+ * @author victor.perez@e-evolution.com
+ * @see FR 1906632
+ *      http://sourceforge.net/tracker/?func=detail&atid=879335&aid=1906632&grou...
+ */
+public class ReportStarter2 implements ProcessCall, ClientProcess {
+    // logger
+    private static CLogger log = CLogger.getCLogger(ReportStarter2.class);
+
+    private static JRViewerProvider viewerProvider = new SwingJRViewerProvider();
+
+    static {
+        System.setProperty("javax.xml.parsers.SAXParserFactory",
+                "org.apache.xerces.jaxp.SAXParserFactoryImpl");
+        System.setProperty("org.xml.sax.driver",
+                "org.apache.xerces.parsers.SAXParser");
+    }
+
+    private ProcessInfo processInfo;
+
+
+    /**
+     * Returns the Server Connection if direct connection is not available (VPN,
+     * WAN, Terminal) and thus query has to be run on server only else return a
+     * direct connection to DB.
+     *
+     * Notes: Need to refactor and integrate in DB if confirmed to be working as
+     * expected.
+     *
+     * @author Ashley Ramdass
+     * @return Connection DB Connection
+     */
+    protected Connection getConnection() {
+        if (DB.isRemoteObjects()) {
+            return new ServerConnection();
+        } else {
+            return DB.getConnectionRW();
+        }
+    }
+
+    static JasperReportCache jasperReportCache;
+
+    void initCache() throws Exception {
+        if (jasperReportCache == null) {
+            jasperReportCache = new JasperReportCache();
+        }
+    }
+
+    /**
+     * Start the process. Called then pressing the Process button in R_Request.
+     * It should only return false, if the function could not be performed as
+     * this causes the process to abort.
+     *
+     * @author rlemeill
+     * @param ctx
+     *            context
+     * @param pi
+     *            Compiere standard process info
+     * @param trx
+     * @return true if success
+     */
+    public boolean startProcess(Properties ctx, ProcessInfo pi, Trx trx) {
+
+        processInfo = pi;
+        String Name = pi.getTitle();
+        int AD_PInstance_ID = pi.getAD_PInstance_ID();
+        int Record_ID = pi.getRecord_ID();
+        log.info("Name=" + Name + "  AD_PInstance_ID=" + AD_PInstance_ID
+                + " Record_ID=" + Record_ID);
+        String trxName = null;
+        if (trx != null) {
+            trxName = trx.getTrxName();
+        }
+        try {
+            initCache();
+            ReportData reportData = getReportData(pi, trxName);
+            if (reportData == null) {
+                reportResult(AD_PInstance_ID, "Can not find report data",
+                        trxName);
+                return false;
+            }
+
+            String reportPath = reportData.getReportFilePath();
+            if ((reportPath == null) || (reportPath.length() == 0)) {
+                throw new RuntimeException("Can not find report - report file name not set");
+            }
+
+            HashMap<String, Object> params = new HashMap(ctx);
+       
+
+            addProcessParameters(AD_PInstance_ID, params, trxName);
+
+            addProcessInfoParameters(params, pi.getParameter());
+            JasperReportCache.FileLoadEnvironment fileLoadEnvironment=jasperReportCache.
                new FileLoadEnvironment(pi, reportPath);
+            JasperReport jasperReport = fileLoadEnvironment.getRootReportFile();
+
+            if (jasperReport != null) {
+                if (Record_ID > 0)
+                    params.put("RECORD_ID", new Integer(Record_ID));
+
+                // contribution from Ricardo (ralexsander)
+                // in iReports you can 'SELECT' AD_Client_ID, AD_Org_ID and
+                // AD_User_ID using only AD_PINSTANCE_ID
+                params.put("AD_PINSTANCE_ID", new Integer(AD_PInstance_ID));
+
+                Language currLang = Env.getLanguage(Env.getCtx());
+                params.put("CURRENT_LANG", currLang.getAD_Language());
+               
+                fileLoadEnvironment.fillParams(params);
+                Connection conn = getConnection();
+                try {
+                    JasperPrint jasperPrint = JasperFillManager.fillReport(
+                            jasperReport, params, conn);
+                    if (reportData.isDirectPrint()) {
+                        log.info("ReportStarter.startProcess print report -"
+                                + jasperPrint.getName());
+
+                        // RF 1906632
+                        if (!processInfo.isBatch())
+                            JasperPrintManager.printReport(jasperPrint, false);
+                        else {
+                            // You can use JasperPrint to create PDF
+                            // Used For the PH
+                            try {
+                                File PDF = File.createTempFile("mail", ".pdf");
+                                JasperExportManager.exportReportToPdfFile(
+                                        jasperPrint, PDF.getAbsolutePath());
+                                processInfo.setPDFReport(PDF);
+                            } catch (IOException e) {
+                                log
+                                        .severe("ReportStarter.startProcess:
                                                Can not make PDF File - "
+                                                + e.getMessage());
+                            }
+                        }
+
+                        // You can use JasperPrint to create PDF
+                        // JasperExportManager.exportReportToPdfFile(jasperPrint,
+                        // "BasicReport.pdf");
+                    } else {
+                        log.info("ReportStarter.startProcess run report -"
+                                + jasperPrint.getName());
+                        JRViewerProvider viewerLauncher = getReportViewerProvider();
+                        viewerLauncher.openViewer(jasperPrint, pi.getTitle()
+                                + " - " + reportPath);
+                    }
+                } finally {
+                    conn.close();
+                }
+            }
+            reportResult(AD_PInstance_ID, null, trxName);
+        } catch (Exception e) {
+            log.log(Level.SEVERE, "Jasper report process error", e);
+            reportResult(AD_PInstance_ID, e.toString(), trxName);
+            return false;
+        }
+
+        return true;
+    }
+
+
+    /**
+     * @author rlemeill
+     * @param AD_PInstance_ID
+     * @param errMsg
+     */
+    protected void reportResult(int AD_PInstance_ID, String errMsg,
+            String trxName) {
+        int result = (errMsg == null) ? 1 : 0;
+        errMsg = (errMsg == null) ? "" : errMsg;
+        String sql = "UPDATE AD_PInstance SET result=" + result
+                + ", errormsg='" + errMsg + "' " + "WHERE AD_PInstance_ID="
+                + AD_PInstance_ID;
+        Statement pstmt = null;
+        try {
+            pstmt = DB.createStatement(ResultSet.TYPE_FORWARD_ONLY,
+                    ResultSet.CONCUR_UPDATABLE, trxName);
+            pstmt.executeUpdate(sql);
+            pstmt.close();
+        } catch (SQLException e) {
+            log.severe(sql + e.getMessage());
+        } finally {
+            DBUtils.close(pstmt);
+        }
+    }
+
+    protected void addProcessParameters(int AD_PInstance_ID,
+            Map<String, Object> params, String trxName) {
+        log.info("");
+        String sql = "SELECT ParameterName, " + "P_String, " + "P_String_To, "
+                + "P_Number, " + "P_Number_To, " + "P_Date, " + "P_Date_To "
+                + "FROM AD_PInstance_Para " + "WHERE AD_PInstance_ID=?";
+        PreparedStatement pstmt = null;
+        ResultSet rs = null;
+        try {
+            pstmt = DB.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY,
+                    ResultSet.CONCUR_READ_ONLY, trxName);
+            pstmt.setInt(1, AD_PInstance_ID);
+            rs = pstmt.executeQuery();
+            while (rs.next()) {
+                String name = rs.getString(1);
+                String pStr = rs.getString(2);
+                String pStrTo = rs.getString(3);
+                // Double pNum = new Double( rs.getDouble(4));
+                // Double pNumTo = new Double( rs.getDouble(5));
+                BigDecimal pNum = rs.getBigDecimal(4);
+                BigDecimal pNumTo = rs.getBigDecimal(5);
+
+                Timestamp pDate = rs.getTimestamp(6);
+                Timestamp pDateTo = rs.getTimestamp(7);
+                if (pStr != null) {
+                    if (pStrTo != null) {
+                        params.put(name + "1", pStr);
+                        params.put(name + "2", pStrTo);
+                    } else {
+                        params.put(name, pStr);
+                    }
+                } else if (pDate != null) {
+                    if (pDateTo != null) {
+                        params.put(name + "1", pDate);
+                        params.put(name + "2", pDateTo);
+                    } else {
+                        params.put(name, pDate);
+                    }
+                } else if (pNum != null) {
+                    if (rs.getBigDecimal(5) != null) {
+                        params.put(name + "1", pNum);
+                        params.put(name + "2", pNumTo);
+                    } else {
+                        params.put(name, pNum);
+                    }
+                }
+            }
+        } catch (SQLException e) {
+            log.severe("Execption; sql = " + sql + "; e.getMessage() = "
+                    + e.getMessage());
+        } finally {
+            DBUtils.close(rs);
+            DBUtils.close(pstmt);
+        }
+    }
+
+    private void addProcessInfoParameters(Map<String, Object> params,
+            ProcessInfoParameter[] para) {
+        if (para != null) {
+            for (int i = 0; i < para.length; i++) {
+                if (para[i].getParameter_To() == null) {
+                    params.put(para[i].getParameterName(), para[i]
+                            .getParameter());
+                } else {
+                    params.put(para[i].getParameterName() + "1", para[i]
+                            .getParameter());
+                    params.put(para[i].getParameterName() + "2", para[i]
+                            .getParameter_To());
+                }
+            }
+        }
+    }
+
+    /**
+     * @author rlemeill
+     * @param ProcessInfo
+     * @return ReportData
+     */
+    public ReportData getReportData(ProcessInfo pi, String trxName) {
+        log.info("");
+        String sql = "SELECT pr.JasperReport, pr.IsDirectPrint "
+                + "FROM AD_Process pr, AD_PInstance pi "
+                + "WHERE pr.AD_Process_ID = pi.AD_Process_ID "
+                + " AND pi.AD_PInstance_ID=?";
+        PreparedStatement pstmt = null;
+        ResultSet rs = null;
+        try {
+            pstmt = DB.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY,
+                    ResultSet.CONCUR_READ_ONLY, trxName);
+            pstmt.setInt(1, pi.getAD_PInstance_ID());
+            rs = pstmt.executeQuery();
+            String path = null;
+            boolean directPrint = false;
+            boolean isPrintPreview = pi.isPrintPreview();
+            if (rs.next()) {
+                path = rs.getString(1);
+
+                if ("Y".equalsIgnoreCase(rs.getString(2))
+                        && !Ini.isPropertyBool(Ini.P_PRINTPREVIEW)
+                        && !isPrintPreview)
+                    directPrint = true;
+            } else {
+                log.severe("data not found; sql = " + sql);
+                return null;
+            }
+
+            return new ReportData(path, directPrint);
+        } catch (SQLException e) {
+            log.severe("sql = " + sql + "; e.getMessage() = " + e.getMessage());
+            return null;
+        } finally {
+            DBUtils.close(rs);
+            DBUtils.close(pstmt);
+        }
+    }
+
+    /**
+     * Set jasper report viewer provider.
+     *
+     * @param provider
+     */
+    public static void setReportViewerProvider(JRViewerProvider provider) {
+        if (provider == null)
+            throw new IllegalArgumentException(
+                    "Cannot set report viewer provider to null");
+        viewerProvider = provider;
+    }
+
+    /**
+     * Get the current jasper report viewer provider
+     *
+     * @return JRViewerProvider
+     */
+    public static JRViewerProvider getReportViewerProvider() {
+        return viewerProvider;
+    }
+
+    class ReportData {
+        private String reportFilePath;
+        private boolean directPrint;
+
+        public ReportData(String reportFilePath, boolean directPrint) {
+            this.reportFilePath = reportFilePath;
+            this.directPrint = directPrint;
+        }
+
+        public String getReportFilePath() {
+            return reportFilePath;
+        }
+
+        public boolean isDirectPrint() {
+            return directPrint;
+        }
+    }
+
+    class JasperData {
+        private JasperReport jasperReport;
+        private File reportDir;
+        private String jasperName;
+        private File jasperFile;
+
+        public JasperData(JasperReport jasperReport, File reportDir,
+                String jasperName, File jasperFile) {
+            this.jasperReport = jasperReport;
+            this.reportDir = reportDir;
+            this.jasperName = jasperName;
+            this.jasperFile = jasperFile;
+        }
+
+        public JasperReport getJasperReport() {
+            return jasperReport;
+        }
+
+        public File getReportDir() {
+            return reportDir;
+        }
+
+        public String getJasperName() {
+            return jasperName;
+        }
+
+        public File getJasperFile() {
+            return jasperFile;
+        }
+    }
+
+    class FileFilter implements FilenameFilter {
+        private String reportStart;
+        private File directory;
+        private String extension;
+
+        public FileFilter(String reportStart, File directory, String extension) {
+            this.reportStart = reportStart;
+            this.directory = directory;
+            this.extension = extension;
+        }
+
+        public boolean accept(File file, String name) {
+            if (file.equals(directory)) {
+                if (name.startsWith(reportStart)) {
+                    int pos = name.lastIndexOf(extension);
+                    if ((pos != -1)
+                            && (pos == (name.length() - extension.length())))
+                        return true;
+                }
+            }
+            return false;
+        }
+    }
+
+}
\ No newline at end of file
Index: JasperReports/src/org/compiere/report/JasperReportCache.java
===================================================================
--- JasperReports/src/org/compiere/report/JasperReportCache.java    (revision 0)
+++ JasperReports/src/org/compiere/report/JasperReportCache.java    (revision 0)
@@ -0,0 +1,397 @@
+package org.compiere.report;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import net.sf.jasperreports.engine.JRException;
+import net.sf.jasperreports.engine.JasperCompileManager;
+import net.sf.jasperreports.engine.JasperReport;
+import net.sf.jasperreports.engine.util.FileResolver;
+import net.sf.jasperreports.engine.util.JRLoader;
+
+import org.compiere.process.ProcessInfo;
+import org.compiere.util.CLogger;
+import org.compiere.util.ZipUtil;
+import org.compiere.utils.DigestOfFile;
+import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+/**
+ * Cache compiled jasper reports on the client. Handle several type of jasper
+ * report file source.
+ *
+ * @author schmidta
+ *
+ */
+public class JasperReportCache {
+    FileSource fileSource = new FileSource();
+
+    @Override
+    public String toString() {
+        return super.toString() + " on " + cacheDir;
+    }
+
+    public JasperReportCache() throws Exception {
+        init();
+    }
+
+    public class FileLoadEnvironment
+    {
+        ProcessInfo pi;
+        String jasperPath;
+        File jasperDir;
+        public FileLoadEnvironment(ProcessInfo pi, String jasperPath) throws
 XPathExpressionException, IOException, SAXException, ParserConfigurationException, JRException {
+            super();
+            this.pi = pi;
+            this.jasperPath=jasperPath;
+            jasperDir=checkLocalCopyOfZip();
+        }
+        public JasperReport getRootReportFile() throws Exception {
+            return (JasperReport) JRLoader.loadObject(new File(jasperDir, "report.jasper"));
+        }
+        public FileResolver getFileResolver() {
+            return new FileResolver()
+            {
+                public File resolveFile(String arg0) {
+                    File ret=new File(jasperDir, arg0);
+                    log.info("Resolving file: "+arg0 +" to: "+ret);
+                    return ret;
+                }
+            };
+        }
+        @Override
+        public String toString() {
+            return "Jasper zip: "+jasperPath;
+        }
+        public void fillParams(Map<String, Object> params) {
+            params.put(net.sf.jasperreports.engine.JRParameter.REPORT_FILE_RESOLVER,
+                    getFileResolver());
+            params.put("SUBREPORT_DIR", "");
+        }
+       
+        /**
+         * Check and create or refresh the local copy of a report file.
+         *
+         * @param reportPath
+         * @return
+         * @throws IOException
+         * @throws ParserConfigurationException
+         * @throws SAXException
+         * @throws XPathExpressionException
+         * @throws JRException
+         */
+        protected File checkLocalCopyOfZip() throws IOException,
+                XPathExpressionException, SAXException,
+                ParserConfigurationException, JRException {
+            try {
+                String name = getFileName(jasperPath);
+                File localFile = new File(cacheDir, name);
+                File zipDir=new File(localFile.getParentFile(),
+                        localFile.getName().substring(0,
+                                localFile.getName().length()-".zip".length()));
+                if (!isFresh(this, localFile, this.jasperPath)) {
+                    UtilFile.deleteRec(zipDir);
+                    fileSource.copyTo(this, jasperPath, localFile);
+                    log.info("unzipping: "+localFile.getAbsolutePath()+" to: "+zipDir.getAbsolutePath());
+                    ZipUtil.unzip(localFile,
+                            zipDir);
+                    compileAll(zipDir);
+                    processed.add(jasperPath);
+                }
+                return zipDir;
+            } catch (Exception e) {
+                throw new RuntimeException("Error resolving "
+                        + this, e);
+            }
+        }
+
+    }
+    interface IFileSource {
+        boolean handles(FileLoadEnvironment loadEnvironment,String fileName) throws Exception;
+
+        String getMd5(FileLoadEnvironment loadEnvironment, String fileName) throws Exception;
+
+        void copyTo(FileLoadEnvironment loadEnvironment, String fileName, File target) throws Exception;
+    }
+
+    class FileSource implements IFileSource {
+        List<IFileSource> types;
+
+        public FileSource() {
+            types = new ArrayList<IFileSource>();
+            types.add(new JasperFileSourceAttachment());
+            types.add(new JasperFileSourceURL());
+            types.add(new JasperFileSourceFile());
+        }
+
+        IFileSource getType(FileLoadEnvironment loadEnvironment, String reportPath) throws Exception {
+            for (IFileSource type : types) {
+                if (type.handles(loadEnvironment, reportPath)) {
+                    return type;
+                }
+            }
+            throw new IOException("report file name is not handled: "
+                    + reportPath);
+        }
+
+        public String getMd5(FileLoadEnvironment loadEnvironment, String fileName) throws Exception {
+            IFileSource type = getType(loadEnvironment, fileName);
+            return type.getMd5(loadEnvironment, fileName);
+        }
+
+        public void copyTo(FileLoadEnvironment loadEnvironment, String fileName, File target) throws Exception {
+            IFileSource type = getType(loadEnvironment, fileName);
+            type.copyTo(loadEnvironment, fileName, target);
+        }
+
+        public boolean handles(FileLoadEnvironment loadEnvironment, String fileName) throws Exception {
+            return getType(loadEnvironment, fileName) != null;
+        }
+    }
+
+    private static CLogger log = CLogger.getCLogger(JasperReportCache.class);
+
+    File cacheDir;
+    boolean inited = false;
+
+    void init() throws Exception {
+        if (!inited) {
+            cacheDir = File.createTempFile("jasper", "cache");
+//            cacheDir=new File("/tmp/jasperCache");
+            UtilFile.deleteRec(cacheDir);
+            cacheDir.mkdir();
+            inited = true;
+        }
+    }
+
+    String getFileName(String reportPath) {
+        int idx = reportPath.lastIndexOf("/");
+        if (idx >= 0) {
+            reportPath = reportPath.substring(idx + 1);
+        }
+        idx = reportPath.lastIndexOf(File.separator);
+        if (idx >= 0) {
+            reportPath = reportPath.substring(idx + 1);
+        }
+        return reportPath;
+    }
+
+    String getFileNameWithoutExtension(String reportPath) {
+        int idx = reportPath.lastIndexOf("/");
+        if (idx >= 0) {
+            reportPath = reportPath.substring(idx + 1);
+        }
+        idx = reportPath.lastIndexOf('.');
+        if (idx >= 0) {
+            reportPath = reportPath.substring(0, idx);
+        }
+        return reportPath;
+    }
+
+    String getFilePath(String reportPath) {
+        int idx = reportPath.lastIndexOf("/");
+        if (idx >= 0) {
+            reportPath = reportPath.substring(0, idx + 1);
+            return reportPath;
+        }
+        return "";
+    }
+
+    String getFileExtension(String reportPath) {
+        int idx = reportPath.lastIndexOf(".");
+        if (idx >= 0) {
+            return reportPath.substring(idx);
+        }
+        return "";
+    }
+    /**
+     * Check and create or refresh the local copy of a report file.
+     *
+     * @param reportPath
+     * @return
+     * @throws IOException
+     * @throws ParserConfigurationException
+     * @throws SAXException
+     * @throws XPathExpressionException
+     * @throws JRException
+     */
+    private File checkLocalCopyOfFile(FileLoadEnvironment loadEnvironment, String reportPath) throws IOException,
+            XPathExpressionException, SAXException,
+            ParserConfigurationException, JRException {
+        try {
+            String name = getFileName(reportPath);
+            String path = getFilePath(reportPath);
+            String extension = getFileExtension(reportPath);
+            File localFile = new File(cacheDir, name);
+            File localJasper = new File(cacheDir,
+                    getFileNameWithoutExtension(reportPath)+".jasper");
+            if (!isFresh(loadEnvironment, localFile, reportPath)) {
+                fileSource.copyTo(loadEnvironment, reportPath, localFile);
+                JasperCompileManager.compileReportToFile(localFile
+                        .getAbsolutePath(), localJasper.getAbsolutePath());
+            }
+            List<String> subreps = getSubreportPaths(localFile);
+            for (String s : subreps) {
+                if (s.endsWith(".jasper")) {
+                    s = s.substring(0, s.length() - ".jasper".length())
+                            + extension;
+                }
+                String subReportPath = path + s;
+                checkLocalCopyOfFile(loadEnvironment, subReportPath);
+            }
+            return localJasper;
+        } catch (Exception e) {
+            throw new RuntimeException("Error resolving Jasper file: "
+                    + reportPath, e);
+        }
+    }
+
+    private void compileAll(File zipDir) throws Exception {
+        FileVisitor.visitRecursive(zipDir, new FileVisitor.Visitor(){
+            public void visitFile(File file) throws Exception{
+                if(file.exists()&&file.isFile()&&file.getName().endsWith(".jrxml"))
+                {
+                    File localJasper = new File(file.getParentFile(),
+                            getFileNameWithoutExtension(file.getName())+".jasper");
+                    JasperCompileManager.compileReportToFile(file
+                                .getAbsolutePath(), localJasper.getAbsolutePath());
+                }
+            }});
+    }
+
+    /**
+     * @author rlemeill Correct the class path if loaded from java web start
+     */
+    private void JWScorrectClassPath() {
+        URL jasperreportsAbsoluteURL = Thread.currentThread()
+                .getContextClassLoader().getResource(
+                        "net/sf/jasperreports/engine");
+        String jasperreportsAbsolutePath = "";
+
+        if (jasperreportsAbsoluteURL.toString().startsWith("jar:http:")
+                || jasperreportsAbsoluteURL.toString().startsWith("jar:https:")) {
+            // Jasper classes are deployed to a webserver (Java Webstart)
+            jasperreportsAbsolutePath = jasperreportsAbsoluteURL.toString()
+                    .split("!")[0].split("jar:")[1];
+
+            // Download the required jasper libraries if they are not already
+            // existing
+            File reqLib = new File(System.getProperty("java.io.tmpdir"),
+                    "CompiereJasperReqs.jar");
+            if (!reqLib.exists() && !(reqLib.length() > 0)) {
+                try {
+                    URL reqLibURL = new URL(jasperreportsAbsolutePath);
+                    InputStream in = reqLibURL.openStream();
+
+                    FileOutputStream fout = new FileOutputStream(reqLib);
+
+                    byte buf[] = new byte[1024];
+                    int s = 0;
+
+                    while ((s = in.read(buf, 0, 1024)) > 0)
+                        fout.write(buf, 0, s);
+
+                    in.close();
+                    fout.flush();
+                    fout.close();
+                } catch (FileNotFoundException e) {
+                    log.warning("Required library not found " + e.getMessage());
+                    reqLib.delete();
+                    reqLib = null;
+                } catch (IOException e) {
+                    log
+                            .severe("I/O error downloading required library from server "
+                                    + e.getMessage());
+                    reqLib.delete();
+                    reqLib = null;
+                }
+            }
+
+            jasperreportsAbsolutePath = reqLib.getAbsolutePath();
+        } else {
+            // Jasper classes are locally available (Local client)
+            jasperreportsAbsolutePath = jasperreportsAbsoluteURL.toString()
+                    .split("!")[0].split("file:")[1];
+        }
+
+        if (jasperreportsAbsolutePath != null
+                && !jasperreportsAbsolutePath.trim().equals("")) {
+            // Check whether the current CLASSPATH already contains our
+            // jasper libraries and dependencies or not.
+            if (System.getProperty("java.class.path").indexOf(
+                    jasperreportsAbsolutePath) < 0) {
+                System.setProperty("java.class.path", System
+                        .getProperty("java.class.path")
+                        + System.getProperty("path.separator")
+                        + jasperreportsAbsolutePath);
+                log.info("Classpath has been corrected to "
+                        + System.getProperty("java.class.path"));
+            }
+        }
+    }
+
+    static List<String> getSubreportPaths(File f)
+            throws XPathExpressionException, SAXException, IOException,
+            ParserConfigurationException {
+        List<String> ret = new ArrayList<String>();
+        String expression = "//subreport/subreportExpression";
+        // Compile the expression to get a XPathExpression object.
+        Document xmlDocument = DocumentBuilderFactory.newInstance()
+                .newDocumentBuilder().parse(f);
+        XPathFactory xPathFactory = XPathFactory.newInstance();
+        // To get an instance of the XPathFactory object itself.
+
+        XPath xPath = xPathFactory.newXPath();
+        // Create an instance of XPath from the factory class.
+        XPathExpression xPathExpression = xPath.compile(expression);
+        Object result = xPathExpression.evaluate(xmlDocument,
+                XPathConstants.NODESET);
+        NodeList l = (NodeList) result;
+        XPathExpression toString = xPath.compile(".");
+        for (int i = 0; i < l.getLength(); ++i) {
+            Object item = l.item(i);
+            String s = "" + toString.evaluate(item);
+            int a = s.indexOf('\"');
+            int b = s.lastIndexOf('\"');
+            if (a >= 0 && b > a) {
+                ret.add(s.substring(a + 1, b));
+            }
+        }
+        return ret;
+    }
+    Set<String> processed=new TreeSet<String>();
+    private boolean isFresh(FileLoadEnvironment loadEnvironment, File localFile, String reportPath)
+            throws Exception {
+        if (!localFile.exists()) {
+            return false;
+        }
+        if(processed.contains(reportPath))
+        {
+            String md5local = DigestOfFile.GetLocalMD5Hash(localFile);
+            String md5remote = fileSource.getMd5(loadEnvironment, reportPath);
+            return md5local.equals(md5remote);
+        }else
+        {
+            return false;
+        }
+    }
+
+}
Index: JasperReports/src/org/compiere/report/JasperFileSourceFile.java
===================================================================
--- JasperReports/src/org/compiere/report/JasperFileSourceFile.java    (revision 0)
+++ JasperReports/src/org/compiere/report/JasperFileSourceFile.java    (revision 0)
@@ -0,0 +1,65 @@
+package org.compiere.report;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.compiere.report.JasperReportCache.FileLoadEnvironment;
+import org.compiere.util.Ini;
+import org.compiere.util.ZipUtil;
+import org.compiere.utils.DigestOfFile;
+
+public class JasperFileSourceFile implements JasperReportCache.IFileSource {
+    private static File REPORT_HOME = null;
+    static {
+        String reportPath = System.getProperty("org.compiere.report.path");
+        if (reportPath == null) {
+            REPORT_HOME = new File(Ini.getAdempiereHome() + File.separator
+                    + "reports");
+        } else {
+            REPORT_HOME = new File(reportPath);
+        }
+    }
+
+    File getFile(FileLoadEnvironment loadEnvironment, String reportPath) throws URISyntaxException {
+        if (reportPath.startsWith("/")) {
+            return new File(reportPath);
+        } else if (reportPath.startsWith("file:/")) {
+            return new File(new URI(reportPath));
+        } else {
+            return new File(REPORT_HOME, reportPath);
+        }
+    }
+
+    public void copyTo(FileLoadEnvironment loadEnvironment, String fileName, File target) throws IOException, URISyntaxException {
+        File source=getFile(loadEnvironment, fileName);
+        copy(source,target);
+    }
+
+    void copy(File inputFile, File outputFile) throws IOException {
+        FileInputStream in = new FileInputStream(inputFile);
+        try {
+            FileOutputStream out = new FileOutputStream(outputFile);
+            try {
+                ZipUtil.copyInputStream(in, out);
+            } finally {
+                out.close();
+            }
+        } finally {
+            in.close();
+        }
+    }
+
+    public String getMd5(FileLoadEnvironment loadEnvironment, String fileName) throws IOException, URISyntaxException {
+        String md5local = DigestOfFile.GetLocalMD5Hash(getFile(loadEnvironment, fileName));
+        return md5local;
+    }
+
+    public boolean handles(FileLoadEnvironment loadEnvironment, String fileName) {
+        return true;
+    }
+
+}
Index: JasperReports/src/org/compiere/report/JasperFileSourceURL.java
===================================================================
--- JasperReports/src/org/compiere/report/JasperFileSourceURL.java    (revision 0)
+++ JasperReports/src/org/compiere/report/JasperFileSourceURL.java    (revision 0)
@@ -0,0 +1,69 @@
+package org.compiere.report;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.compiere.model.MAttachment;
+import org.compiere.model.MAttachmentEntry;
+import org.compiere.model.MProcess;
+import org.compiere.report.JasperReportCache.FileLoadEnvironment;
+import org.compiere.util.Env;
+import org.compiere.utils.DigestOfFile;
+
+public class JasperFileSourceURL implements JasperReportCache.IFileSource {
+
+    MAttachmentEntry getAttachment(FileLoadEnvironment loadEnvironment,
+            String fileName) throws IOException
+    {
+        String name = fileName.substring(prefix.length()).trim();
+        if(name.startsWith("//"))
+        {
+            name=name.substring(2);
+        }
+        MProcess process = new MProcess(Env.getCtx(),
+                loadEnvironment.
+                pi.getAD_Process_ID(),
+                loadEnvironment.pi.getTransactionName());
+        MAttachment attachment = process.getAttachment();
+        if (attachment != null) {
+            MAttachmentEntry[] entries = attachment.getEntries();
+            for (int i = 0; i < entries.length; i++) {
+                if (entries[i].getName().equals(name)) {
+                    return entries[i];
+                }
+            }
+        }
+        throw new IOException("Process attachment does not exist: "+fileName+" on process: "+loadEnvironment.pi.getAD_Process_ID());
+    }
+    String prefix="attachment:";
+    public void copyTo(FileLoadEnvironment loadEnvironment, String fileName, File target) throws Exception {
+        MAttachmentEntry entry=getAttachment(loadEnvironment, fileName);
+        FileOutputStream fos=new FileOutputStream(target);
+        try
+        {
+            fos.write(entry.getData());
+        }finally
+        {
+            fos.close();
+        }
+    }
+
+    public String getMd5(FileLoadEnvironment loadEnvironment, String fileName) throws Exception {
+        MAttachmentEntry entry=getAttachment(loadEnvironment, fileName);
+        return DigestOfFile.getMD5Hash(entry.getData());
+    }
+
+    public boolean handles(FileLoadEnvironment loadEnvironment, String fileName) throws Exception {
+        return false;
+//        java.net.URL url=new URL(fileName);
+//        InputStream is=url.openStream();
+//        try
+//        {
+//            return is!=null;
+//        }finally
+//        {
+//            is.close();
+//        }
+    }
+}
Index: JasperReports/src/org/compiere/report/UtilFile.java
===================================================================
--- JasperReports/src/org/compiere/report/UtilFile.java    (revision 0)
+++ JasperReports/src/org/compiere/report/UtilFile.java    (revision 0)
@@ -0,0 +1,57 @@
+package org.compiere.report;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+
+public class UtilFile {
+    public static String loadResourceAsString(Class<?> c, String res) throws IOException
+    {
+        URL url=c.getResource(res);
+        InputStream is=url.openStream();
+        try
+        {
+            StringBuilder ret=new StringBuilder();
+            InputStreamReader isr=new InputStreamReader(is,"UTF-8");
+            char[] chs=new char[1024];
+            int count;
+            while((count=isr.read(chs))>0)
+            {
+                ret.append(chs,0,count);
+            }
+            return ret.toString();
+        }finally
+        {
+            is.close();
+        }
+    }
+
+    public static void deleteRec(File toDelete) throws Exception {
+        if(!toDelete.exists())
+        {
+            return;
+        }
+        final List<File> l=new ArrayList<File>();
+        l.add(toDelete);
+        FileVisitor.visitRecursive(toDelete, new FileVisitor.Visitor()
+        {
+            public void visitFile(File file) throws Exception {
+                l.add(file);
+            }
+        });
+        Collections.reverse(l);
+        for(File f:l)
+        {
+            if(!f.delete())
+            {
+                throw new IOException("Cannot delete file: "+f);
+            }
+        }
+    }
+}
Index: JasperReports/src/org/compiere/report/JasperFileSourceAttachment.java
===================================================================
--- JasperReports/src/org/compiere/report/JasperFileSourceAttachment.java    (revision 0)
+++ JasperReports/src/org/compiere/report/JasperFileSourceAttachment.java    (revision 0)
@@ -0,0 +1,60 @@
+package org.compiere.report;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.compiere.model.MAttachment;
+import org.compiere.model.MAttachmentEntry;
+import org.compiere.model.MProcess;
+import org.compiere.report.JasperReportCache.FileLoadEnvironment;
+import org.compiere.util.Env;
+import org.compiere.utils.DigestOfFile;
+
+public class JasperFileSourceAttachment implements JasperReportCache.IFileSource {
+
+    MAttachmentEntry getAttachment(FileLoadEnvironment loadEnvironment,
+            String fileName) throws IOException
+    {
+        String name = fileName.substring(prefix.length()).trim();
+        if(name.startsWith("//"))
+        {
+            name=name.substring(2);
+        }
+        MProcess process = new MProcess(Env.getCtx(),
+                loadEnvironment.
+                pi.getAD_Process_ID(),
+                loadEnvironment.pi.getTransactionName());
+        MAttachment attachment = process.getAttachment();
+        if (attachment != null) {
+            MAttachmentEntry[] entries = attachment.getEntries();
+            for (int i = 0; i < entries.length; i++) {
+                if (entries[i].getName().equals(name)) {
+                    return entries[i];
+                }
+            }
+        }
+        throw new IOException("Process attachment does not exist: "+fileName+" on process: "+loadEnvironment.pi.getAD_Process_ID());
+    }
+    String prefix="attachment:";
+    public void copyTo(FileLoadEnvironment loadEnvironment, String fileName, File target) throws Exception {
+        MAttachmentEntry entry=getAttachment(loadEnvironment, fileName);
+        FileOutputStream fos=new FileOutputStream(target);
+        try
+        {
+            fos.write(entry.getData());
+        }finally
+        {
+            fos.close();
+        }
+    }
+
+    public String getMd5(FileLoadEnvironment loadEnvironment, String fileName) throws Exception {
+        MAttachmentEntry entry=getAttachment(loadEnvironment, fileName);
+        return DigestOfFile.getMD5Hash(entry.getData());
+    }
+
+    public boolean handles(FileLoadEnvironment loadEnvironment, String fileName) throws Exception {
+        return fileName.startsWith(prefix);
+    }
+}
Index: JasperReports/build.xml
===================================================================
--- JasperReports/build.xml    (revision 5753)
+++ JasperReports/build.xml    (working copy)
@@ -64,7 +64,7 @@
           <pathelement path="../lib/CSTools.jar"/>
           <pathelement path="../looks/CLooks.jar"/>
           <pathelement path="../lib/oracle.jar"/>
-          <pathelement path="../JasperReportsTools/lib/jasperreports-1.3.0.jar"/>
+          <pathelement path="../JasperReportsTools/lib/jasperreports-3.0.0.jar"/>
           <pathelement path="../JasperReportsTools/lib/commons-digester-1.7.jar"/>
           <pathelement path="../tools/lib/commons-collections-3.1.jar"/>
           <pathelement path="../tools/lib/commons-logging.jar"/>
@@ -124,14 +124,14 @@
           </patternset>
     </unjar>
       <!-- futur dev jfree chart but it needs scriptlets
-      <unjar src="../tools/lib/jfreechart-0.9.21.jar" dest="${needed.dir}" />
+      <unjar src="../tools/lib/jfreechart-1.0.3.jar" dest="${needed.dir}" />
       -->
       <jar jarfile="${dist.dir}/${needed.jar.name}.jar"
       excludes="**/*.jbx"
       index="yes"
         duplicate="preserve">
       <fileset dir="${needed.dir}"/>
-        <zipfileset src="../JasperReportsTools/lib/jasperreports-1.3.0.jar" >
+        <zipfileset src="../JasperReportsTools/lib/jasperreports-3.0.0.jar" >
         <patternset refid="manifest.exclude"/>
       </zipfileset>
         <zipfileset src="../JasperReportsTools/lib/commons-digester-1.7.jar" >
Index: base/src/org/compiere/util/ZipUtil.java
===================================================================
--- base/src/org/compiere/util/ZipUtil.java    (revision 5753)
+++ base/src/org/compiere/util/ZipUtil.java    (working copy)
@@ -435,5 +435,63 @@
             System.err.println(ex);
         }
     }
+   
+    public static final void copyInputStream(InputStream in, OutputStream out)
+            throws IOException {
+        byte[] buffer = new byte[1024];
+        int len;

+        while ((len = in.read(buffer)) >= 0)
+            out.write(buffer, 0, len);
+
+        in.close();
+        out.close();
+    }
+
+    static CLogger log = CLogger.getCLogger(ZipUtil.class);
+
+    /**
+     * Unzip a zip file into a directory.
+     *
+     * @param zipF
+     * @param toDir
+     */
+    public static void unzip(File zipF, File toDir) {
+
+        try {
+            ZipFile zipFile = new ZipFile(zipF);
+
+            Enumeration<? extends ZipEntry> entries = zipFile.entries();
+            log.info("unzipping file: " + zipF.getAbsolutePath() + " todir: "
+                    + toDir.getAbsolutePath());
+
+            while (entries.hasMoreElements()) {
+                ZipEntry entry = entries.nextElement();
+                File target = new File(toDir, entry.getName());
+                if (entry.isDirectory()) {
+                    // Assume directories are stored parents first then
+                    // children.
+                    System.err.println("Extracting directory: "
+                            + entry.getName());
+                    // This is not robust, just for demonstration purposes.
+                    target.mkdirs();
+                } else {
+                    log.info("unzipping file to: " + target.getAbsolutePath());
+                    target.getParentFile().mkdirs();
+                    copyInputStream(zipFile.getInputStream(entry),
+                            new BufferedOutputStream(new FileOutputStream(
+                                    target)));
+
+                }
+            }
+
+            zipFile.close();
+        } catch (IOException ioe) {
+            System.err.println("Unhandled exception:");
+            ioe.printStackTrace();
+            return;
+        }
+    }
+
+
 }    //    ZipUtil
Index: client/build.xml
===================================================================
--- client/build.xml    (revision 5753)
+++ client/build.xml    (working copy)
@@ -28,7 +28,7 @@
     <pathelement path="../lib/customization.jar"/>
     <pathelement path="../lib/patches.jar"/>
     <pathelement path="../lib/jcommon-1.0.5.jar"/>
-    <pathelement path="../lib/jfreechart-1.0.2.jar"/>
+    <pathelement path="../lib/jfreechart-1.0.3.jar"/>
     <pathelement path="../looks/CLooks.jar"/>
     <pathelement path="../extend/Extend.jar"/>
     <pathelement path="../tools/lib/j2ee.jar"/>