001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors.
006 *
007 * Project Info:  http://www.jfree.org/jfreechart/index.html
008 *
009 * This library is free software; you can redistribute it and/or modify it
010 * under the terms of the GNU Lesser General Public License as published by
011 * the Free Software Foundation; either version 2.1 of the License, or
012 * (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful, but
015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017 * License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this library; if not, write to the Free Software
021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
022 * USA.
023 *
024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025 * in the United States and other countries.]
026 *
027 * ---------------------
028 * ServletUtilities.java
029 * ---------------------
030 * (C) Copyright 2002-2008, by Richard Atkinson and Contributors.
031 *
032 * Original Author:  Richard Atkinson;
033 * Contributor(s):   J?rgen Hoffman;
034 *                   David Gilbert (for Object Refinery Limited);
035 *                   Douglas Clayton;
036 *
037 * Changes
038 * -------
039 * 19-Aug-2002 : Version 1;
040 * 20-Apr-2003 : Added additional sendTempFile method to allow MIME type
041 *               specification and modified original sendTempFile method to
042 *               automatically set MIME type for JPEG and PNG files
043 * 23-Jun-2003 : Added additional sendTempFile method at the request of
044 *               J?rgen Hoffman;
045 * 07-Jul-2003 : Added more header information to streamed images;
046 * 19-Aug-2003 : Forced images to be stored in the temporary directory defined
047 *               by System property java.io.tmpdir, rather than default (RA);
048 * 24-Mar-2004 : Added temp filename prefix attribute (DG);
049 * 09-Mar-2005 : Added "one time" file option (DG);
050 * ------------- JFREECHART 1.0.x RELEASED ------------------------------------
051 * 10-Jan-2006 : Updated API docs and reformatted (DG);
052 * 13-Sep-2006 : Format date in response header in English, not locale default
053 *               (see bug 1557141) (DG);
054 *
055 */
056
057package org.jfree.chart.servlet;
058
059import java.io.BufferedInputStream;
060import java.io.BufferedOutputStream;
061import java.io.File;
062import java.io.FileInputStream;
063import java.io.FileNotFoundException;
064import java.io.IOException;
065import java.text.SimpleDateFormat;
066import java.util.Date;
067import java.util.Locale;
068import java.util.TimeZone;
069
070import javax.servlet.http.HttpServletResponse;
071import javax.servlet.http.HttpSession;
072
073import org.jfree.chart.ChartRenderingInfo;
074import org.jfree.chart.ChartUtilities;
075import org.jfree.chart.JFreeChart;
076
077/**
078 * Utility class used for servlet related JFreeChart operations.
079 */
080public class ServletUtilities {
081
082    /** The filename prefix. */
083    private static String tempFilePrefix = "jfreechart-";
084
085    /** A prefix for "one time" charts. */
086    private static String tempOneTimeFilePrefix = "jfreechart-onetime-";
087
088    /**
089     * Returns the prefix for the temporary file names generated by this class.
090     *
091     * @return The prefix (never <code>null</code>).
092     */
093    public static String getTempFilePrefix() {
094        return ServletUtilities.tempFilePrefix;
095    }
096
097    /**
098     * Sets the prefix for the temporary file names generated by this class.
099     *
100     * @param prefix  the prefix (<code>null</code> not permitted).
101     */
102    public static void setTempFilePrefix(String prefix) {
103        if (prefix == null) {
104            throw new IllegalArgumentException("Null 'prefix' argument.");
105        }
106        ServletUtilities.tempFilePrefix = prefix;
107    }
108
109    /**
110     * Returns the prefix for "one time" temporary file names generated by
111     * this class.
112     *
113     * @return The prefix.
114     */
115    public static String getTempOneTimeFilePrefix() {
116        return ServletUtilities.tempOneTimeFilePrefix;
117    }
118
119    /**
120     * Sets the prefix for the "one time" temporary file names generated by
121     * this class.
122     *
123     * @param prefix  the prefix (<code>null</code> not permitted).
124     */
125    public static void setTempOneTimeFilePrefix(String prefix) {
126        if (prefix == null) {
127            throw new IllegalArgumentException("Null 'prefix' argument.");
128        }
129        ServletUtilities.tempOneTimeFilePrefix = prefix;
130    }
131
132    /**
133     * Saves the chart as a PNG format file in the temporary directory.
134     *
135     * @param chart  the JFreeChart to be saved.
136     * @param width  the width of the chart.
137     * @param height  the height of the chart.
138     * @param session  the HttpSession of the client (if <code>null</code>, the
139     *                 temporary file is marked as "one-time" and deleted by
140     *                 the {@link DisplayChart} servlet right after it is
141     *                 streamed to the client).
142     *
143     * @return The filename of the chart saved in the temporary directory.
144     *
145     * @throws IOException if there is a problem saving the file.
146     */
147    public static String saveChartAsPNG(JFreeChart chart, int width, int height,
148            HttpSession session) throws IOException {
149
150        return ServletUtilities.saveChartAsPNG(chart, width, height, null,
151                session);
152
153    }
154
155    /**
156     * Saves the chart as a PNG format file in the temporary directory and
157     * populates the {@link ChartRenderingInfo} object which can be used to
158     * generate an HTML image map.
159     *
160     * @param chart  the chart to be saved (<code>null</code> not permitted).
161     * @param width  the width of the chart.
162     * @param height  the height of the chart.
163     * @param info  the ChartRenderingInfo object to be populated
164     *              (<code>null</code> permitted).
165     * @param session  the HttpSession of the client (if <code>null</code>, the
166     *                 temporary file is marked as "one-time" and deleted by
167     *                 the {@link DisplayChart} servlet right after it is
168     *                 streamed to the client).
169     *
170     * @return The filename of the chart saved in the temporary directory.
171     *
172     * @throws IOException if there is a problem saving the file.
173     */
174    public static String saveChartAsPNG(JFreeChart chart, int width, int height,
175            ChartRenderingInfo info, HttpSession session) throws IOException {
176
177        if (chart == null) {
178            throw new IllegalArgumentException("Null 'chart' argument.");
179        }
180        ServletUtilities.createTempDir();
181        String prefix = ServletUtilities.tempFilePrefix;
182        if (session == null) {
183            prefix = ServletUtilities.tempOneTimeFilePrefix;
184        }
185        File tempFile = File.createTempFile(prefix, ".png",
186                new File(System.getProperty("java.io.tmpdir")));
187        ChartUtilities.saveChartAsPNG(tempFile, chart, width, height, info);
188        if (session != null) {
189            ServletUtilities.registerChartForDeletion(tempFile, session);
190        }
191        return tempFile.getName();
192
193    }
194
195    /**
196     * Saves the chart as a JPEG format file in the temporary directory.
197     * <p>
198     * SPECIAL NOTE: Please avoid using JPEG as an image format for charts,
199     * it is a "lossy" format that introduces visible distortions in the
200     * resulting image - use PNG instead.  In addition, note that JPEG output
201     * is supported by JFreeChart only for JRE 1.4.2 or later.
202     *
203     * @param chart  the JFreeChart to be saved.
204     * @param width  the width of the chart.
205     * @param height  the height of the chart.
206     * @param session  the HttpSession of the client (if <code>null</code>, the
207     *                 temporary file is marked as "one-time" and deleted by
208     *                 the {@link DisplayChart} servlet right after it is
209     *                 streamed to the client).
210     *
211     * @return The filename of the chart saved in the temporary directory.
212     *
213     * @throws IOException if there is a problem saving the file.
214     */
215    public static String saveChartAsJPEG(JFreeChart chart, int width,
216                                         int height, HttpSession session)
217            throws IOException {
218
219        return ServletUtilities.saveChartAsJPEG(chart, width, height, null,
220                session);
221
222    }
223
224    /**
225     * Saves the chart as a JPEG format file in the temporary directory and
226     * populates the <code>ChartRenderingInfo</code> object which can be used
227     * to generate an HTML image map.
228     * <p>
229     * SPECIAL NOTE: Please avoid using JPEG as an image format for charts,
230     * it is a "lossy" format that introduces visible distortions in the
231     * resulting image - use PNG instead.  In addition, note that JPEG output
232     * is supported by JFreeChart only for JRE 1.4.2 or later.
233     *
234     * @param chart  the chart to be saved (<code>null</code> not permitted).
235     * @param width  the width of the chart
236     * @param height  the height of the chart
237     * @param info  the ChartRenderingInfo object to be populated
238     * @param session  the HttpSession of the client (if <code>null</code>, the
239     *                 temporary file is marked as "one-time" and deleted by
240     *                 the {@link DisplayChart} servlet right after it is
241     *                 streamed to the client).
242     *
243     * @return The filename of the chart saved in the temporary directory
244     *
245     * @throws IOException if there is a problem saving the file.
246     */
247    public static String saveChartAsJPEG(JFreeChart chart, int width,
248            int height, ChartRenderingInfo info, HttpSession session)
249            throws IOException {
250
251        if (chart == null) {
252            throw new IllegalArgumentException("Null 'chart' argument.");
253        }
254
255        ServletUtilities.createTempDir();
256        String prefix = ServletUtilities.tempFilePrefix;
257        if (session == null) {
258            prefix = ServletUtilities.tempOneTimeFilePrefix;
259        }
260        File tempFile = File.createTempFile(prefix, ".jpeg",
261                new File(System.getProperty("java.io.tmpdir")));
262        ChartUtilities.saveChartAsJPEG(tempFile, chart, width, height, info);
263        if (session != null) {
264            ServletUtilities.registerChartForDeletion(tempFile, session);
265        }
266        return tempFile.getName();
267
268    }
269
270    /**
271     * Creates the temporary directory if it does not exist.  Throws a
272     * <code>RuntimeException</code> if the temporary directory is
273     * <code>null</code>.  Uses the system property <code>java.io.tmpdir</code>
274     * as the temporary directory.  This sounds like a strange thing to do but
275     * my temporary directory was not created on my default Tomcat 4.0.3
276     * installation.  Could save some questions on the forum if it is created
277     * when not present.
278     */
279    protected static void createTempDir() {
280        String tempDirName = System.getProperty("java.io.tmpdir");
281        if (tempDirName == null) {
282            throw new RuntimeException("Temporary directory system property "
283                    + "(java.io.tmpdir) is null.");
284        }
285
286        // create the temporary directory if it doesn't exist
287        File tempDir = new File(tempDirName);
288        if (!tempDir.exists()) {
289            tempDir.mkdirs();
290        }
291    }
292
293    /**
294     * Adds a {@link ChartDeleter} object to the session object with the name
295     * <code>JFreeChart_Deleter</code> if there is not already one bound to the
296     * session and adds the filename to the list of charts to be deleted.
297     *
298     * @param tempFile  the file to be deleted.
299     * @param session  the HTTP session of the client.
300     */
301    protected static void registerChartForDeletion(File tempFile,
302            HttpSession session) {
303
304        //  Add chart to deletion list in session
305        if (session != null) {
306            ChartDeleter chartDeleter
307                = (ChartDeleter) session.getAttribute("JFreeChart_Deleter");
308            if (chartDeleter == null) {
309                chartDeleter = new ChartDeleter();
310                session.setAttribute("JFreeChart_Deleter", chartDeleter);
311            }
312            chartDeleter.addChart(tempFile.getName());
313        }
314        else {
315            System.out.println("Session is null - chart will not be deleted");
316        }
317    }
318
319    /**
320     * Binary streams the specified file in the temporary directory to the
321     * HTTP response in 1KB chunks.
322     *
323     * @param filename  the name of the file in the temporary directory.
324     * @param response  the HTTP response object.
325     *
326     * @throws IOException  if there is an I/O problem.
327     */
328    public static void sendTempFile(String filename,
329            HttpServletResponse response) throws IOException {
330
331        File file = new File(System.getProperty("java.io.tmpdir"), filename);
332        ServletUtilities.sendTempFile(file, response);
333    }
334
335    /**
336     * Binary streams the specified file to the HTTP response in 1KB chunks.
337     *
338     * @param file  the file to be streamed.
339     * @param response  the HTTP response object.
340     *
341     * @throws IOException if there is an I/O problem.
342     */
343    public static void sendTempFile(File file, HttpServletResponse response)
344            throws IOException {
345
346        String mimeType = null;
347        String filename = file.getName();
348        if (filename.length() > 5) {
349            if (filename.substring(filename.length() - 5,
350                    filename.length()).equals(".jpeg")) {
351                mimeType = "image/jpeg";
352            }
353            else if (filename.substring(filename.length() - 4,
354                    filename.length()).equals(".png")) {
355                mimeType = "image/png";
356            }
357        }
358        ServletUtilities.sendTempFile(file, response, mimeType);
359    }
360
361    /**
362     * Binary streams the specified file to the HTTP response in 1KB chunks.
363     *
364     * @param file  the file to be streamed.
365     * @param response  the HTTP response object.
366     * @param mimeType  the mime type of the file, null allowed.
367     *
368     * @throws IOException if there is an I/O problem.
369     */
370    public static void sendTempFile(File file, HttpServletResponse response,
371                                    String mimeType) throws IOException {
372
373        if (file.exists()) {
374            BufferedInputStream bis = new BufferedInputStream(
375                    new FileInputStream(file));
376
377            //  Set HTTP headers
378            if (mimeType != null) {
379                response.setHeader("Content-Type", mimeType);
380            }
381            response.setHeader("Content-Length", String.valueOf(file.length()));
382            SimpleDateFormat sdf = new SimpleDateFormat(
383                    "EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH);
384            sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
385            response.setHeader("Last-Modified",
386                    sdf.format(new Date(file.lastModified())));
387
388            BufferedOutputStream bos = new BufferedOutputStream(
389                    response.getOutputStream());
390            byte[] input = new byte[1024];
391            boolean eof = false;
392            while (!eof) {
393                int length = bis.read(input);
394                if (length == -1) {
395                    eof = true;
396                }
397                else {
398                    bos.write(input, 0, length);
399                }
400            }
401            bos.flush();
402            bis.close();
403            bos.close();
404        }
405        else {
406            throw new FileNotFoundException(file.getAbsolutePath());
407        }
408        return;
409    }
410
411    /**
412     * Perform a search/replace operation on a String
413     * There are String methods to do this since (JDK 1.4)
414     *
415     * @param inputString  the String to have the search/replace operation.
416     * @param searchString  the search String.
417     * @param replaceString  the replace String.
418     *
419     * @return The String with the replacements made.
420     */
421    public static String searchReplace(String inputString,
422                                       String searchString,
423                                       String replaceString) {
424
425        int i = inputString.indexOf(searchString);
426        if (i == -1) {
427            return inputString;
428        }
429
430        String r = "";
431        r += inputString.substring(0, i) + replaceString;
432        if (i + searchString.length() < inputString.length()) {
433            r += searchReplace(inputString.substring(i + searchString.length()),
434                    searchString, replaceString);
435        }
436
437        return r;
438    }
439
440}