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