001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2020, 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 
025 * Other names may be trademarks of their respective owners.]
026 *
027 * ---------------------
028 * ServletUtilities.java
029 * ---------------------
030 * (C) Copyright 2002-2016, 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 * 03-Jul-2013 : Use ParamChecks (DG);
055 *
056 */
057
058package org.jfree.chart.servlet;
059
060import java.io.BufferedInputStream;
061import java.io.BufferedOutputStream;
062import java.io.File;
063import java.io.FileInputStream;
064import java.io.FileNotFoundException;
065import java.io.IOException;
066import java.text.SimpleDateFormat;
067import java.util.Date;
068import java.util.Locale;
069import java.util.TimeZone;
070
071import javax.servlet.http.HttpServletResponse;
072import javax.servlet.http.HttpSession;
073
074import org.jfree.chart.ChartRenderingInfo;
075import org.jfree.chart.ChartUtils;
076import org.jfree.chart.JFreeChart;
077import org.jfree.chart.util.Args;
078
079/**
080 * Utility class used for servlet related JFreeChart operations.
081 */
082public class ServletUtilities {
083
084    /** The filename prefix. */
085    private static String tempFilePrefix = "jfreechart-";
086
087    /** A prefix for "one time" charts. */
088    private static String tempOneTimeFilePrefix = "jfreechart-onetime-";
089
090    /**
091     * Returns the prefix for the temporary file names generated by this class.
092     *
093     * @return The prefix (never {@code null}).
094     */
095    public static String getTempFilePrefix() {
096        return ServletUtilities.tempFilePrefix;
097    }
098
099    /**
100     * Sets the prefix for the temporary file names generated by this class.
101     *
102     * @param prefix  the prefix ({@code null} not permitted).
103     */
104    public static void setTempFilePrefix(String prefix) {
105        Args.nullNotPermitted(prefix, "prefix");
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} not permitted).
124     */
125    public static void setTempOneTimeFilePrefix(String prefix) {
126        Args.nullNotPermitted(prefix, "prefix");
127        ServletUtilities.tempOneTimeFilePrefix = prefix;
128    }
129
130    /**
131     * Saves the chart as a PNG format file in the temporary directory.
132     *
133     * @param chart  the JFreeChart to be saved.
134     * @param width  the width of the chart.
135     * @param height  the height of the chart.
136     * @param session  the HttpSession of the client (if {@code null}, the
137     *                 temporary file is marked as "one-time" and deleted by
138     *                 the {@link DisplayChart} servlet right after it is
139     *                 streamed to the client).
140     *
141     * @return The filename of the chart saved in the temporary directory.
142     *
143     * @throws IOException if there is a problem saving the file.
144     */
145    public static String saveChartAsPNG(JFreeChart chart, int width, int height,
146            HttpSession session) throws IOException {
147
148        return ServletUtilities.saveChartAsPNG(chart, width, height, null,
149                session);
150
151    }
152
153    /**
154     * Saves the chart as a PNG format file in the temporary directory and
155     * populates the {@link ChartRenderingInfo} object which can be used to
156     * generate an HTML image map.
157     *
158     * @param chart  the chart to be saved ({@code null} not permitted).
159     * @param width  the width of the chart.
160     * @param height  the height of the chart.
161     * @param info  the ChartRenderingInfo object to be populated
162     *              ({@code null} permitted).
163     * @param session  the HttpSession of the client (if {@code null}, the
164     *                 temporary file is marked as "one-time" and deleted by
165     *                 the {@link DisplayChart} servlet right after it is
166     *                 streamed to the client).
167     *
168     * @return The filename of the chart saved in the temporary directory.
169     *
170     * @throws IOException if there is a problem saving the file.
171     */
172    public static String saveChartAsPNG(JFreeChart chart, int width, int height,
173            ChartRenderingInfo info, HttpSession session) throws IOException {
174
175        Args.nullNotPermitted(chart, "chart");
176        ServletUtilities.createTempDir();
177        String prefix = ServletUtilities.tempFilePrefix;
178        if (session == null) {
179            prefix = ServletUtilities.tempOneTimeFilePrefix;
180        }
181        File tempFile = File.createTempFile(prefix, ".png",
182                new File(System.getProperty("java.io.tmpdir")));
183        ChartUtils.saveChartAsPNG(tempFile, chart, width, height, info);
184        if (session != null) {
185            ServletUtilities.registerChartForDeletion(tempFile, session);
186        }
187        return tempFile.getName();
188
189    }
190
191    /**
192     * Saves the chart as a JPEG format file in the temporary directory.
193     * <p>
194     * SPECIAL NOTE: Please avoid using JPEG as an image format for charts,
195     * it is a "lossy" format that introduces visible distortions in the
196     * resulting image - use PNG instead.  In addition, note that JPEG output
197     * is supported by JFreeChart only for JRE 1.4.2 or later.
198     *
199     * @param chart  the JFreeChart to be saved.
200     * @param width  the width of the chart.
201     * @param height  the height of the chart.
202     * @param session  the HttpSession of the client (if {@code null}, the
203     *                 temporary file is marked as "one-time" and deleted by
204     *                 the {@link DisplayChart} servlet right after it is
205     *                 streamed to the client).
206     *
207     * @return The filename of the chart saved in the temporary directory.
208     *
209     * @throws IOException if there is a problem saving the file.
210     */
211    public static String saveChartAsJPEG(JFreeChart chart, int width,
212                                         int height, HttpSession session)
213            throws IOException {
214
215        return ServletUtilities.saveChartAsJPEG(chart, width, height, null,
216                session);
217
218    }
219
220    /**
221     * Saves the chart as a JPEG format file in the temporary directory and
222     * populates the {@code ChartRenderingInfo} object which can be used
223     * to generate an HTML image map.
224     * <p>
225     * SPECIAL NOTE: Please avoid using JPEG as an image format for charts,
226     * it is a "lossy" format that introduces visible distortions in the
227     * resulting image - use PNG instead.  In addition, note that JPEG output
228     * is supported by JFreeChart only for JRE 1.4.2 or later.
229     *
230     * @param chart  the chart to be saved ({@code null} not permitted).
231     * @param width  the width of the chart
232     * @param height  the height of the chart
233     * @param info  the ChartRenderingInfo object to be populated
234     * @param session  the HttpSession of the client (if {@code null}, the
235     *                 temporary file is marked as "one-time" and deleted by
236     *                 the {@link DisplayChart} servlet right after it is
237     *                 streamed to the client).
238     *
239     * @return The filename of the chart saved in the temporary directory
240     *
241     * @throws IOException if there is a problem saving the file.
242     */
243    public static String saveChartAsJPEG(JFreeChart chart, int width,
244            int height, ChartRenderingInfo info, HttpSession session)
245            throws IOException {
246
247        Args.nullNotPermitted(chart, "chart");
248        ServletUtilities.createTempDir();
249        String prefix = ServletUtilities.tempFilePrefix;
250        if (session == null) {
251            prefix = ServletUtilities.tempOneTimeFilePrefix;
252        }
253        File tempFile = File.createTempFile(prefix, ".jpeg",
254                new File(System.getProperty("java.io.tmpdir")));
255        ChartUtils.saveChartAsJPEG(tempFile, chart, width, height, info);
256        if (session != null) {
257            ServletUtilities.registerChartForDeletion(tempFile, session);
258        }
259        return tempFile.getName();
260
261    }
262
263    /**
264     * Creates the temporary directory if it does not exist.  Throws a
265     * {@code RuntimeException} if the temporary directory is
266     * {@code null}.  Uses the system property {@code java.io.tmpdir}
267     * as the temporary directory.  This sounds like a strange thing to do but
268     * my temporary directory was not created on my default Tomcat 4.0.3
269     * installation.  Could save some questions on the forum if it is created
270     * when not present.
271     */
272    protected static void createTempDir() {
273        String tempDirName = System.getProperty("java.io.tmpdir");
274        if (tempDirName == null) {
275            throw new RuntimeException("Temporary directory system property "
276                    + "(java.io.tmpdir) is null.");
277        }
278
279        // create the temporary directory if it doesn't exist
280        File tempDir = new File(tempDirName);
281        if (!tempDir.exists()) {
282            tempDir.mkdirs();
283        }
284    }
285
286    /**
287     * Adds a {@link ChartDeleter} object to the session object with the name
288     * {@code JFreeChart_Deleter} if there is not already one bound to the
289     * session and adds the filename to the list of charts to be deleted.
290     *
291     * @param tempFile  the file to be deleted.
292     * @param session  the HTTP session of the client.
293     */
294    protected static void registerChartForDeletion(File tempFile,
295            HttpSession session) {
296
297        //  Add chart to deletion list in session
298        if (session != null) {
299            ChartDeleter chartDeleter
300                = (ChartDeleter) session.getAttribute("JFreeChart_Deleter");
301            if (chartDeleter == null) {
302                chartDeleter = new ChartDeleter();
303                session.setAttribute("JFreeChart_Deleter", chartDeleter);
304            }
305            chartDeleter.addChart(tempFile.getName());
306        }
307        else {
308            System.out.println("Session is null - chart will not be deleted");
309        }
310    }
311
312    /**
313     * Binary streams the specified file in the temporary directory to the
314     * HTTP response in 1KB chunks.
315     *
316     * @param filename  the name of the file in the temporary directory.
317     * @param response  the HTTP response object.
318     *
319     * @throws IOException  if there is an I/O problem.
320     */
321    public static void sendTempFile(String filename,
322            HttpServletResponse response) throws IOException {
323
324        File file = new File(System.getProperty("java.io.tmpdir"), filename);
325        ServletUtilities.sendTempFile(file, response);
326    }
327
328    /**
329     * Binary streams the specified file to the HTTP response in 1KB chunks.
330     *
331     * @param file  the file to be streamed.
332     * @param response  the HTTP response object.
333     *
334     * @throws IOException if there is an I/O problem.
335     */
336    public static void sendTempFile(File file, HttpServletResponse response)
337            throws IOException {
338
339        String mimeType = null;
340        String filename = file.getName();
341        if (filename.length() > 5) {
342            if (filename.substring(filename.length() - 5,
343                    filename.length()).equals(".jpeg")) {
344                mimeType = "image/jpeg";
345            }
346            else if (filename.substring(filename.length() - 4,
347                    filename.length()).equals(".png")) {
348                mimeType = "image/png";
349            }
350        }
351        ServletUtilities.sendTempFile(file, response, mimeType);
352    }
353
354    /**
355     * Binary streams the specified file to the HTTP response in 1KB chunks.
356     *
357     * @param file  the file to be streamed.
358     * @param response  the HTTP response object.
359     * @param mimeType  the mime type of the file, null allowed.
360     *
361     * @throws IOException if there is an I/O problem.
362     */
363    public static void sendTempFile(File file, HttpServletResponse response,
364                                    String mimeType) throws IOException {
365
366        if (file.exists()) {
367            BufferedInputStream bis = new BufferedInputStream(
368                    new FileInputStream(file));
369
370            //  Set HTTP headers
371            if (mimeType != null) {
372                response.setHeader("Content-Type", mimeType);
373            }
374            response.setHeader("Content-Length", String.valueOf(file.length()));
375            SimpleDateFormat sdf = new SimpleDateFormat(
376                    "EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH);
377            sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
378            response.setHeader("Last-Modified",
379                    sdf.format(new Date(file.lastModified())));
380
381            BufferedOutputStream bos = new BufferedOutputStream(
382                    response.getOutputStream());
383            byte[] input = new byte[1024];
384            boolean eof = false;
385            while (!eof) {
386                int length = bis.read(input);
387                if (length == -1) {
388                    eof = true;
389                }
390                else {
391                    bos.write(input, 0, length);
392                }
393            }
394            bos.flush();
395            bis.close();
396            bos.close();
397        }
398        else {
399            throw new FileNotFoundException(file.getAbsolutePath());
400        }
401    }
402
403    /**
404     * Perform a search/replace operation on a String
405     * There are String methods to do this since (JDK 1.4)
406     *
407     * @param inputString  the String to have the search/replace operation.
408     * @param searchString  the search String.
409     * @param replaceString  the replace String.
410     *
411     * @return The String with the replacements made.
412     */
413    public static String searchReplace(String inputString,
414                                       String searchString,
415                                       String replaceString) {
416
417        int i = inputString.indexOf(searchString);
418        if (i == -1) {
419            return inputString;
420        }
421
422        String r = "";
423        r += inputString.substring(0, i) + replaceString;
424        if (i + searchString.length() < inputString.length()) {
425            r += searchReplace(inputString.substring(i + searchString.length()),
426                    searchString, replaceString);
427        }
428
429        return r;
430    }
431
432}